pax_global_header00006660000000000000000000000064126607120450014515gustar00rootroot0000000000000052 comment=255c31fe31c418fd93cd2dde5941e0f923ab51ca tesseract-3.04.01/000077500000000000000000000000001266071204500136575ustar00rootroot00000000000000tesseract-3.04.01/.gitignore000066400000000000000000000016621266071204500156540ustar00rootroot00000000000000*~ # Windows *.user *.log *.tlog *.cache *.obj *.sdf *.opensdf *.lastbuildstate *.unsuccessfulbuild *.suo *.res *.ipch *.manifest vs2010/DLL_Debug/* vs2010/DLL_Release/* vs2010/LIB_Debug/* vs2010/LIB_Release/* vs2010/LIB_OpenCL_Release/* vs2010/LIB_OpenCL_Debug/* # Linux # ignore local configuration config.* config/* Makefile Makefile.in *.m4 # ignore help scripts/files configure libtool stamp-h1 tesseract.pc config_auto.h doc/html/* api/tesseract training/ambiguous_words training/classifier_tester training/cntraining training/combine_tessdata training/dawg2wordlist training/mftraining training/set_unicharset_properties training/shapeclustering training/text2image training/unicharset_extractor training/wordlist2dawg *.patch # ignore compilation files build/* */.deps/* */.libs/* *.lo *.la *.o *.Plo *.a *.class *.jar # tessdata *.cube.* *.tesseract_cube.* *.traineddata # OpenCL tesseract_opencl_profile_devices.dat kernel*.bin tesseract-3.04.01/AUTHORS000066400000000000000000000006701266071204500147320ustar00rootroot00000000000000Ray Smith (lead developer) Ahmad Abdulkader Rika Antonova Nicholas Beato Samuel Charron Phil Cheatle Simon Crouch David Eger Sheelagh Huddleston Dan Johnson Thomas Kielbus Dar-Shyang Lee Zongyi (Joe) Liu Robert Moss Chris Newton Michael Reimer Marius Renn Raquel Romano Christy Russon Shobhit Saxena Mark Seaman Faisal Shafait Hiroshi Takenaka Ranjith Unnikrishnan Joern Wanke Ping Ping Xiu Andrew Ziem Oscar Zuniga tesseract-3.04.01/COPYING000066400000000000000000000017601266071204500147160ustar00rootroot00000000000000This package contains the Tesseract Open Source OCR Engine. Originally developed at Hewlett Packard Laboratories Bristol and at Hewlett Packard Co, Greeley Colorado, all the code in this distribution is now licensed under the Apache License: ** 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. Other Dependencies and Licenses: ================================ Tesseract uses Leptonica library (http://leptonica.com/) with a very weakly restricted copyright license (http://leptonica.com/about-the-license.html) tesseract-3.04.01/ChangeLog000066400000000000000000000230551266071204500154360ustar00rootroot000000000000002014-02-04 v3.03 * Added new training tool text2image to generate box/tif file pairs from text and truetype fonts. * Added support for PDF output with searchable text. * Removed entire IMAGE class and all code in image directory. * Tesseract executable: support for output to stdout; limited support for one page images from stdin (especially on Windows) * 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. * More training source data included. 2012-02-01 - v3.02 * Moved ResultIterator/PageIterator to ccmain. * Added Right-to-left/Bidi capability in the output iterators for Hebrew/Arabic. * Added paragraph detection in layout analysis/post OCR. * Fixed inconsistent xheight during training and over-chopping. * Added simultaneous multi-language capability. * Refactored top-level word recognition module. * Added experimental equation detector. * Improved handling of resolution from input images. * Blamer module added for error analysis. * Cleaned up externally used namespace by removing includes from baseapi.h. * Removed dead memory mangagement code. * Tidied up constraints on control parameters. * Added support for ShapeTable in classifier and training. * Refactored class pruner. * Fixed training leaks and randomness. * Major improvements to layout analysis for better image detection, diacritic detection, better textline finding, better tabstop finding. * Improved line detection and removal. * Added fixed pitch chopper for CJK. * Added UNICHARSET to WERD_CHOICE to make mult-language handling easier. * Fixed problems with internally scaled images. * Added page and bbox to string in tr files to identify source of training data better. * Fixes to Hindi Shiroreka splitter. * Added word bigram correction. * Reduced stack memory consumption and eliminated some ugly typedefs. * Added new uniform classifier API. * Added new training error counter. * Fixed endian bug in dawg reader. * Many other fixes, including the way in which the chopper finds chops and messes with the outline while it does so. 2010-11-29 - V3.01 * Removed old/dead serialise/deserialze methods on *LISTIZED classes. * Total rewrite of DENORM to better encapsulate operation and make for potential to extract features from images. * Thread-safety! Moved all critical globals and statics to members of the appropriate class. Tesseract is now thread-safe (multiple instances can be used in parallel in multiple threads.) with the minor exception that some control parameters are still global and affect all threads. * Added Cube, a new recognizer for Arabic. Cube can also be used in combination with normal Tesseract for other languages with an improvement in accuracy at the cost of (much) lower speed. *There is no training module for Cube yet.* * `OcrEngineMode` in `Init` replaces `AccuracyVSpeed` to control cube. * Greatly improved segmentation search with consequent accuracy and speed improvements, especially for Chinese. * Added `PageIterator` and `ResultIterator` as cleaner ways to get the full results out of Tesseract, that are not currently provided by any of the `TessBaseAPI::Get*` methods. All other methods, such as the `ETEXT_STRUCT` in particular are deprecated and will be deleted in the future. * ApplyBoxes totally rewritten to make training easier. It can now cope with touching/overlapping training characters, and a new boxfile format allows word boxes instead of character boxes, BUT to use that you have to have already boostrapped the language with character boxes. "Cyclic dependency" on traineddata. * Auto orientation and script detection added to page layout analysis. * Deleted *lots* of dead code. * Fixxht module replaced with scalable data-driven module. * Output font characteristics accuracy improved. * Removed the double conversion at each classification. * Upgraded oldest structs to be classes and deprecated PBLOB. * Removed non-deterministic baseline fit. * Added fixed length dawgs for Chinese. * Handling of vertical text improved. * Handling of leader dots improved. * Table detection greatly improved. * Fixed a couple of memory leaks. * Fixed font labels on output text. (Not perfect, but a lot better than before.) * Cleanup and more bug fixes * Special treatments for Hindi. * Support for build in VS2010 with Microsoft Windows SDK for Windows 7 (thanks to Michael Lutz) 2010-09-21 - V3.00 * Preparations for thread safety: * Changed TessBaseAPI methods to be non-static * Created a class hierarchy for the directories to hold instance data, and began moving code into the classes. * Moved thresholding code to a separate class. * Added major new page layout analysis module. * Added HOCR output (issues 221, 263: thanks to amkryukov). * Added Leptonica as main image I/O and handling. Currently optional, but in future releases linking with Leptonica will be mandatory. * Ambiguity table rewritten to allow definite replacements in place of fix_quotes. * Added TessdataManager to combine data files into a single file. * Some dead code deleted. * VC++6 no longer supported. It can't cope with the use of templates. * Many more languages added. * Doxygenation of most of the function header comments. * Added man pages. * Added bash completion script (issue 247: thanks to neskiem) * Fix integer overview in thresholding (issue 366: thanks to Cyanide.Drake) * Add Danish Fraktur support (issues 300, 360: thanks to dsl602230@vip.cybercity.dk) * Fix file pointer leak (issue 359, thanks to yukihiro.nakadaira) * Fix an error using user-words (Issue 345: thanks to max.markin) * Fix a memory leak in tablefind.cpp (Issue 342, thanks to zdravco) * Fix a segfault due to double fclose (Issue 320, thanks to souther) * Fix an automake error (Issue 318, thanks to ichanjz) * Fix a Win32 crash on fileFormatIsTiff() (Issues 304, 316, 317, 330, 347, 349, 352: thanks to nguyenq87, max.markin, zdenop) * Fixed a number of errors in newer (stricter) versions of VC++ (Issues 301, among others) 2009-06-30 - V2.04 * Integrated bug fixes and patches and misc changes for portability. * Integrated a patch to remove some of the "access" macros. * Removed dependence on lua from the viewer, speeding it up dramatically. * Fixed the viewer so it compiles and runs properly! * Specifically fixing issues: 1, 63, 67, 71, 76, 81, 82, 106, 111, 112, 128, 129, 130, 133, 135, 142, 143, 145, 147, 153, 154, 160, 165, 170, 175, 177, 187, 192, 195, 199, 201, 205, 209, 108, 169 2008-04-22 - V2.03 * Fixed crash introduced in 2.02. * Fixed lack of tessembedded.cpp in distribution. * Added test for leptonica header files and conditional test for lib. 2008-04-21 - V2.02 (again) * Fixed namespace collisions with jpeg library (INT32). * Portability fixes for Windows for new code. * Updates to autoconf system for new code. 2008-01-23 - V2.02 * Improvements to clustering, training and classifier. * Major internationalization improvements for large-character-set * languages, eg Kannada. * Removed some compiler warnings. * Added multipage tiff support for training and running. * Updated graphics output to talk to new java-based viewer. * Added ability to save n-best lists. * Added leptonica support for more file types. * Improved Init/End to make them safe. * Reduced memory use of dictionaries. * Added some new APIs to TessBaseAPI. 2007-08-27 - V2.01 * Fixed UTF8 input problems with box file reader. * Fixed various infinite loops and crashes in dawg code. * Removed include of config_auto.h from host.h. * Added automatic wctype encoding to unicharset_extractor. * Fixed dawg table too full error. * Removed svn files from tarball. * Added new functions to tessdll. * Increased maximum utf8 string in a classification result to 8. 2007-07-02 - V2.00 * Converted internal character handling to UTF8. * Trained with 6 languages. * Added unicharset_extractor, wordlist2dawg. * Added boxfile creation mode. * Added UNLV regression test capability. * Fixed problems with copyright and registered symbols. * Fixed extern "C" declarations problem. 2007-05-15 - V1.04 * Added dll exports for Windows. * Fixed name collisions with stl etc. * Made some preliminary changes ready for unicodeization. * Several bug fixes discovered during unicodeization. 2007-02-02 - V1.03 * Added mftraining and cntraining. * Added baseapi with adaptive thresholding for grey and color. * Fixed many memory leaks. * Fixed several bugs including lack of use of adaptive classifier. * Added ifdefs to eliminate graphics code and add embedded platform support. * Incorporated several patches, including 64-bit builds, Mac builds. * Minor accuracy improvements. 2006-10-04 - V1.02 * Removed dependency on Aspirin. * Fixed a few missing Apache license headers. * Removed $log. 2006-09-07 - V1.01. * Added mfcpch.cpp and getopt.cpp for VC++. * Fixed problem with greyscale images and no libtiff. * Stopped debug window from being used for the usage output. * Fixed load of inttemp for big-endian architectures. * Fixed some Mac compilation issues. 2006-06-16 - V1.0 of open source Tesseract checked-in. tesseract-3.04.01/INSTALL000066400000000000000000000221331266071204500147110ustar00rootroot00000000000000Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== These are generic installation instructions. First you need to run `./autogen.sh', that creates `configure' script. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. (Caching is disabled by default to prevent problems with accidental use of stale cache files.) If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You only need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not support the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the `--target=TYPE' option to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc will cause the specified gcc to be used as the C compiler (unless it is overridden in the site shell script). `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. tesseract-3.04.01/INSTALL.GIT000066400000000000000000000023601266071204500153330ustar00rootroot00000000000000autotools (LINUX/UNIX...) ====================== If you have cloned Tesseract from Github, you must generate the configure script. If you have tesseract 3.0x installation in your system, please remove it before new build. Known dependencies for training tools (excluding leptonica): * compiler with c++ support * pango-devel * cairo-devel * icu-devel So, the steps for making Tesseract are: $ ./autogen.sh $ ./configure $ make $ sudo make install $ make training $ sudo make training-install You need to install at least English language data file to TESSDATA_PREFIX directory. All language data files can be retrieved from git repository: $ git clone https://github.com/tesseract-ocr/tessdata.git tesseract-ocr.tessdata (Repository it huge - more that 1.2Gb. You do not need to download all languages) To compile ScrollView.jar you need to download piccolo2d-core-3.0.jar and piccolo2d-extras-3.0.jar[1] and place them to tesseract/java. Than run: $ make ScrollView.jar and follow instruction on Viewer Debugging wiki[2]. [1] http://search.maven.org/#search|ga|1|g%3A%22org.piccolo2d%22 [2] https://github.com/tesseract-ocr/tesseract/wiki/ViewerDebugging WINDOWS ======= Please read http://vorba.ch/2014/tesseract-3.03-vs2013.html tesseract-3.04.01/Makefile.am000066400000000000000000000040001266071204500157050ustar00rootroot00000000000000## run autogen.sh to create Makefile.in from this file ACLOCAL_AMFLAGS = -I m4 if ENABLE_TRAINING TRAINING_SUBDIR = training training: $(MAKE) @cd "$(top_builddir)/training" && $(MAKE) training-install: @cd "$(top_builddir)/training" && $(MAKE) install training-uninstall: @cd "$(top_builddir)/training" && $(MAKE) uninstall clean-local: @cd "$(top_builddir)/training" && $(MAKE) clean else training: @echo "Need to reconfigure project, so there are no errors" endif .PHONY: install-langs ScrollView.jar install-jars $(TRAINING_SUBDIR) SUBDIRS = ccutil viewer cutil opencl ccstruct dict classify wordrec textord if !NO_CUBE_BUILD SUBDIRS += neural_networks/runtime cube endif SUBDIRS += ccmain api . tessdata doc EXTRA_DIST = ReleaseNotes README.md\ aclocal.m4 config configure.ac autogen.sh contrib \ tesseract.pc.in $(TRAINING_SUBDIR) java doc testing DIST_SUBDIRS = $(SUBDIRS) $(TRAINING_SUBDIR) uninstall-hook: rm -rf $(DESTDIR)$(includedir) dist-hook: # Need to remove .svn directories from directories # added using EXTRA_DIST. $(distdir)/tessdata would in # theory suffice. rm -rf `find $(distdir) -name .svn` rm -rf `find $(distdir) -name .git` rm -rf `find $(distdir) -name .deps` rm -rf `find $(distdir) -name .libs` rm -rf `find $(distdir) -name *.o` rm -rf `find $(distdir) -name *.lo` rm -rf `find $(distdir) -name *.la` rm -rf `find $(distdir)/training -executable -type f` rm -rf $(distdir)/doc/html/* ScrollView.jar: @cd "$(top_builddir)/java" && $(MAKE) $@ install-jars: @cd "$(top_builddir)/java" && $(MAKE) $@ doc-dummy: doc: doc-dummy -srcdir="$(top_srcdir)" builddir="$(top_builddir)" \ version="@PACKAGE_VERSION@" name="@PACKAGE_NAME@" \ doxygen $(top_srcdir)/doc/Doxyfile doc-pack: doc -chmod a+r $(top_srcdir)/doc/html/* @tar --create --directory=$(top_srcdir)/doc/html --verbose --file=- . | gzip -c -9 > $(top_srcdir)/@PACKAGE_NAME@-@PACKAGE_VERSION@-doc-html.tar.gz; doc-clean: rm -rf $(top_srcdir)/doc/html/* pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = tesseract.pc tesseract-3.04.01/Makefile.in000066400000000000000000000735441266071204500157410ustar00rootroot00000000000000# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @NO_CUBE_BUILD_FALSE@am__append_1 = neural_networks/runtime cube subdir = . DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/configure $(am__configure_deps) \ $(top_srcdir)/config/config.h.in $(srcdir)/tesseract.pc.in \ AUTHORS COPYING ChangeLog INSTALL NEWS config/config.guess \ config/config.sub config/depcomp config/install-sh \ config/missing config/ltmain.sh \ $(top_srcdir)/config/config.guess \ $(top_srcdir)/config/config.sub \ $(top_srcdir)/config/install-sh $(top_srcdir)/config/ltmain.sh \ $(top_srcdir)/config/missing ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config_auto.h CONFIG_CLEAN_FILES = tesseract.pc CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkgconfigdir)" DATA = $(pkgconfig_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ cscope distdir dist dist-all distcheck am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags CSCOPE = cscope DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_CPPFLAGS = @AM_CPPFLAGS@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AM_LDFLAGS = @AM_LDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FRAMEWORK_OPENCL = @FRAMEWORK_OPENCL@ GENERIC_API_VERSION = @GENERIC_API_VERSION@ GENERIC_LIBRARY_NAME = @GENERIC_LIBRARY_NAME@ GENERIC_LIBRARY_VERSION = @GENERIC_LIBRARY_VERSION@ GENERIC_MAJOR_VERSION = @GENERIC_MAJOR_VERSION@ GENERIC_RELEASE = @GENERIC_RELEASE@ GENERIC_VERSION = @GENERIC_VERSION@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBLEPT_HEADERSDIR = @LIBLEPT_HEADERSDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCL_CPPFLAGS = @OPENCL_CPPFLAGS@ OPENCL_LDFLAGS = @OPENCL_LDFLAGS@ OPENMP_CXXFLAGS = @OPENMP_CXXFLAGS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_DATE = @PACKAGE_DATE@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_YEAR = @PACKAGE_YEAR@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ ACLOCAL_AMFLAGS = -I m4 @ENABLE_TRAINING_TRUE@TRAINING_SUBDIR = training SUBDIRS = ccutil viewer cutil opencl ccstruct dict classify wordrec \ textord $(am__append_1) ccmain api . tessdata doc EXTRA_DIST = ReleaseNotes README.md\ aclocal.m4 config configure.ac autogen.sh contrib \ tesseract.pc.in $(TRAINING_SUBDIR) java doc testing DIST_SUBDIRS = $(SUBDIRS) $(TRAINING_SUBDIR) pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = tesseract.pc all: config_auto.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: am--refresh: Makefile @: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): config_auto.h: stamp-h1 @if test ! -f $@; then rm -f stamp-h1; else :; fi @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) stamp-h1; else :; fi stamp-h1: $(top_srcdir)/config/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status config_auto.h $(top_srcdir)/config/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config_auto.h stamp-h1 tesseract.pc: $(top_builddir)/config.status $(srcdir)/tesseract.pc.in cd $(top_builddir) && $(SHELL) ./config.status $@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs distclean-libtool: -rm -f libtool config.lt install-pkgconfigDATA: $(pkgconfig_DATA) @$(NORMAL_INSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ done uninstall-pkgconfigDATA: @$(NORMAL_UNINSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$(top_distdir)" distdir="$(distdir)" \ dist-hook -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-tarZ: distdir tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build \ && ../configure --srcdir=.. --prefix="$$dc_install_base" \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am check: check-recursive all-am: Makefile $(DATA) config_auto.h installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(pkgconfigdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." @ENABLE_TRAINING_FALSE@clean-local: clean: clean-recursive clean-am: clean-generic clean-libtool clean-local mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f Makefile distclean-am: clean-am distclean-generic distclean-hdr \ distclean-libtool distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-pkgconfigDATA install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-pkgconfigDATA @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) uninstall-hook .MAKE: $(am__recursive_targets) all install-am install-strip \ uninstall-am .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--refresh check check-am clean clean-cscope clean-generic \ clean-libtool clean-local cscope cscopelist-am ctags ctags-am \ dist dist-all dist-bzip2 dist-gzip dist-hook dist-lzip \ dist-shar dist-tarZ dist-xz dist-zip distcheck distclean \ distclean-generic distclean-hdr distclean-libtool \ distclean-tags distcleancheck distdir distuninstallcheck dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-pkgconfigDATA install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am uninstall-hook \ uninstall-pkgconfigDATA @ENABLE_TRAINING_TRUE@training: @ENABLE_TRAINING_TRUE@ $(MAKE) @ENABLE_TRAINING_TRUE@ @cd "$(top_builddir)/training" && $(MAKE) @ENABLE_TRAINING_TRUE@training-install: @ENABLE_TRAINING_TRUE@ @cd "$(top_builddir)/training" && $(MAKE) install @ENABLE_TRAINING_TRUE@training-uninstall: @ENABLE_TRAINING_TRUE@ @cd "$(top_builddir)/training" && $(MAKE) uninstall @ENABLE_TRAINING_TRUE@clean-local: @ENABLE_TRAINING_TRUE@ @cd "$(top_builddir)/training" && $(MAKE) clean @ENABLE_TRAINING_FALSE@training: @ENABLE_TRAINING_FALSE@ @echo "Need to reconfigure project, so there are no errors" .PHONY: install-langs ScrollView.jar install-jars $(TRAINING_SUBDIR) uninstall-hook: rm -rf $(DESTDIR)$(includedir) dist-hook: # Need to remove .svn directories from directories # added using EXTRA_DIST. $(distdir)/tessdata would in # theory suffice. rm -rf `find $(distdir) -name .svn` rm -rf `find $(distdir) -name .git` rm -rf `find $(distdir) -name .deps` rm -rf `find $(distdir) -name .libs` rm -rf `find $(distdir) -name *.o` rm -rf `find $(distdir) -name *.lo` rm -rf `find $(distdir) -name *.la` rm -rf `find $(distdir)/training -executable -type f` rm -rf $(distdir)/doc/html/* ScrollView.jar: @cd "$(top_builddir)/java" && $(MAKE) $@ install-jars: @cd "$(top_builddir)/java" && $(MAKE) $@ doc-dummy: doc: doc-dummy -srcdir="$(top_srcdir)" builddir="$(top_builddir)" \ version="@PACKAGE_VERSION@" name="@PACKAGE_NAME@" \ doxygen $(top_srcdir)/doc/Doxyfile doc-pack: doc -chmod a+r $(top_srcdir)/doc/html/* @tar --create --directory=$(top_srcdir)/doc/html --verbose --file=- . | gzip -c -9 > $(top_srcdir)/@PACKAGE_NAME@-@PACKAGE_VERSION@-doc-html.tar.gz; doc-clean: rm -rf $(top_srcdir)/doc/html/* # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: tesseract-3.04.01/NEWS000066400000000000000000000000551266071204500143560ustar00rootroot00000000000000Stub file. To be populated at a later stage. tesseract-3.04.01/README.md000066400000000000000000000077141266071204500151470ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/tesseract-ocr/tesseract.svg?branch=master)](https://travis-ci.org/tesseract-ocr/tesseract) [![Build status](https://ci.appveyor.com/api/projects/status/miah0ikfsf0j3819?svg=true)](https://ci.appveyor.com/project/zdenop/tesseract/) For the latest online version of the README.md see: https://github.com/tesseract-ocr/tesseract/blob/master/README.md #About This package contains an OCR engine - `libtesseract` and a command line program - `tesseract`. The lead developer is Ray Smith. The maintainer is Zdenko Podobny. For a list of contributors see [AUTHORS](https://github.com/tesseract-ocr/tesseract/blob/master/AUTHORS) and github's log of [contributors](https://github.com/tesseract-ocr/tesseract/graphs/contributors). Tesseract has unicode (UTF-8) support, and can recognize more than 100 languages "out of the box". It can be trained to recognize other languages. See [Tesseract Training](https://github.com/tesseract-ocr/tesseract/wiki/TrainingTesseract) for more information. Tesseract supports various output formats: plain-text, hocr(html), pdf. This project does not include a GUI application. If you need one, please see the [3rdParty](https://github.com/tesseract-ocr/tesseract/wiki/3rdParty) wiki page. You should note that in many cases, in order to get better OCR results, you'll need to [improve the quality](https://github.com/tesseract-ocr/tesseract/wiki/ImproveQuality) of the image you are giving Tesseract. The latest stable version is 3.04, released in July 2015. #Brief history Tesseract was originally developed at Hewlett-Packard Laboratories Bristol and at Hewlett-Packard Co, Greeley Colorado between 1985 and 1994, with some more changes made in 1996 to port to Windows, and some C++izing in 1998. In 2005 Tesseract was open sourced by HP. Since 2006 it is developed by Google. [Release Notes](https://github.com/tesseract-ocr/tesseract/wiki/ReleaseNotes) #For developers Developers can use `libtesseract` [C](https://github.com/tesseract-ocr/tesseract/blob/master/api/capi.h) or [C++](https://github.com/tesseract-ocr/tesseract/blob/master/api/baseapi.h) API to build their own application. If you need bindings to `libtesseract` for other programming languages, please see the [wrapper](https://github.com/tesseract-ocr/tesseract/wiki/AddOns#tesseract-wrappers) section on AddOns wiki page. Documentation of Tesseract generated from source code by doxygen can be found on [tesseract-ocr.github.io](http://tesseract-ocr.github.io/). #License The code in this repository is 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. **NOTE**: This software depends on other packages that may be licensed under different open source licenses. #Installing Tesseract You can either [Install Tesseract via pre-built binary package](https://github.com/tesseract-ocr/tesseract/wiki) or [build it from source](https://github.com/tesseract-ocr/tesseract/wiki/Compiling). #Running Tesseract Basic command line usage: tesseract imagename outputbase [-l lang] [-psm pagesegmode] [configfiles...] For more information about the various command line options use `tesseract --help` or `man tesseract`. #Support Mailing-lists: * [tesseract-ocr](https://groups.google.com/d/forum/tesseract-ocr) - For tesseract users. * [tesseract-dev](https://groups.google.com/d/forum/tesseract-dev) - For tesseract developers. Please read the [FAQ](https://github.com/tesseract-ocr/tesseract/wiki/FAQ) before asking any question in the mailing-list or reporting an issue. tesseract-3.04.01/ReleaseNotes000066400000000000000000000320021266071204500161700ustar00rootroot00000000000000= Tesseract release notes Feb 01 2012 - V3.02 = * Added Right-to-left/Bidi capability in the output iterators for Hebrew/Arabic. * Added paragraph detection in layout analysis/post OCR. * Added simultaneous multi-language capability. * Added experimental equation detector. * Major improvements to layout analysis for better image detection, diacritic detection, better textline finding, better tabstop finding. * Improved line detection and removal. * Added fixed pitch chopper for CJK. * Added word bigram correction. * Added new uniform classifier API. * Added new training error counter. * More detailed changes recorded in ChangeLog. = Tesseract release notes Oct 21 2011 - V3.01 = * Thread-safety! Moved all critical globals and statics to members of the appropriate class. Tesseract is now thread-safe (multiple instances can be used in parallel in multiple threads.) with the minor exception that some control parameters are still global and affect all threads. * Added Cube, a new recognizer for Arabic. Cube can also be used in combination with normal Tesseract for other languages with an improvement in accuracy at the cost of (much) lower speed. *There is no training module for Cube yet.* * `OcrEngineMode` in `Init` replaces `AccuracyVSpeed` to control cube. * Greatly improved segmentation search with consequent accuracy and speed improvements, especially for Chinese. * Added `PageIterator` and `ResultIterator` as cleaner ways to get the full results out of Tesseract, that are not currently provided by any of the `TessBaseAPI::Get*` methods. All other methods, such as the `ETEXT_STRUCT` in particular are deprecated and will be deleted in the future. * ApplyBoxes totally rewritten to make training easier. It can now cope with touching/overlapping training characters, and a new boxfile format allows word boxes instead of character boxes, BUT to use that you have to have already boostrapped the language with character boxes. "Cyclic dependency" on traineddata. * Auto orientation and script detection added to page layout analysis. * Deleted *lots* of dead code. * Fixxht module replaced with scalable data-driven module. * Output font characteristics accuracy improved. * Removed the double conversion at each classification. * Upgraded oldest structs to be classes and deprecated PBLOB. * Removed non-deterministic baseline fit. * Added fixed length dawgs for Chinese. * Handling of vertical text improved. * Handling of leader dots improved. * Table detection greatly improved. * Fixed a couple of memory leaks. * Fixed font labels on output text. (Not perfect, but a lot better than before.) * Cleanup and more bug fixes * Special treatments for Hindi. * Support for build in VS2010 with Microsoft Windows SDK for Windows 7 (thanks to Michael Lutz) Tesseract release notes Sep 30 2010 - V3.00 * Preparations for thread safety: * Changed TessBaseAPI methods to be non-static * Created a class hierarchy for the directories to hold instance data, and began moving code into the classes. * Moved thresholding code to a separate class. * Added major new page layout analysis module. * Added HOCR output. * Added Leptonica as main image I/O and handling. Currently optional, but in future releases linking with Leptonica will be mandatory. * Ambiguity table rewritten to allow definite replacements in place of fix_quotes. * Added TessdataManager to combine data files into a single file. * Some dead code deleted. * VC++6 no longer supported. It can't cope with the use of templates. * Many more languages added. * Doxygenation of most of the function header comments. Tesseract release notes June 30 2009 - V2.04 Integrated patches for portability and to remove some of the "access" macros. Removed dependence on lua from the viewer making it a *lot* faster. Also the viewer now compiles and works (on Linux.) Fixed the following issues: 1, 63, 67, 71, 76, 79, 81, 82, 84, 106, 108, 111, 112, 128, 129, 130, 133, 135, 142, 143, 145, 146, 147, 153, 154, 160, 165, 169, 170, 175, 177, 187, 192, 195, 199, 201, 205, 209. This is the last version to support VC++6! This may also be the last version to compile without leptonica! Windows version now outputs to stderr by default, fixing a lot of the problems with lack of visible meaningful error messages. Tesseract release notes April 22 2008 - V2.03 2.02 was unrunnable, due to a last-minute "simple" change. 2.03 fixes the problem and also adds an include check for leptonica to make it more usable. Tesseract release notes April 21 2008 - V2.02 Improvements to clustering, training and classifier. Major internationalization improvements for large-character-set languages, eg Kannada. Removed some compiler warnings. Added multipage tiff support for training and running. Updated graphics output to talk to new java-based viewer. Added ability to save n-best lists. Added leptonica support for more file types. Improved Init/End to make them safe. Reduced memory use of dictionaries. Added some new APIs to TessBaseAPI. Fixed namespace collisions with jpeg library (INT32). Portability fixes for Windows for new code. Updates to autoconf system for new code. Tesseract release notes August 27 2007 - V2.01 Fixed UTF8 input problems with box file reader. Fixed various infinite loops and crashes in dawg code. Removed include of config_auto.h from host.h. Added automatic wctype encoding to unicharset_extractor. Fixed dawg table too full error. Removed svn files from tarball. Added new functions to tessdll. Increased maximum utf8 string in a classification result to 8. Tesseract release notes July 17, 2007 - V2.00 First release of the International version. This version recognizes the following languages: English - eng French - fra Italian - ita German - deu Spanish - spa Dutch - nld The language codes follow ISO 639-2. The default language is English. To recognize another language: tesseract inputimage outputbase -l langcode To train on a new language, see separate documentation. More languages will be appearing over time. List of changes in this release: Converted internal character handling to UTF8. Trained with 6 languages. Added unicharset_extractor, wordlist2dawg. Added boxfile creation mode. Added UNLV regression test capability. Fixed problems with copyright and registered symbols. Fixed extern "C" declarations problem. Made some improvements to consistency of accuracy across platforms. Added vc++ express support. Instructions for downloading and building version 2.00. Things have changed quite a bit since the previous versions so please read carefully. *All users* The tarballs are split into pieces. tesseract-2.00.tar.gz contains all the source code. tesseract-2.00..tar.gt contains the data files for . You need at least one of these or tesseract will not work. tesseract-2.00.exe.tar.gz is not for the 'exe' language. It is windows executables. They are built with VC++ express and come with absolutely no warranty. If they work for you then great, otherwise get visual C++ express (and the platform sdk) and build from the source. *Non-windows users* As with 1.04, this version works with make install. *New* there is a tesseract.spec for making rpms. (Thanks to Andrew Ziem for the help.) It might work with your OS if you know how to do that sort of thing. If you are linking to the libraries, as with Ocropus, there is now a single master library called libtesseract_full.a. *Windows users* If you are building from the sources, there are still dsw and dsp files for vc++6 and also sln and vcproj files for vc++ express. The dll has been updated to allow input of non-binary images. (Thanks to Glen of Jetsoft.) Tesseract release notes May 15, 2007 - V1.04. === Windows users only === Added a dll interface for windows. Thanks to Glen at Jetsoft for contributing this. To use the dll, include tessdll.h, import tessdll.lib and put tessdll.dll somewhere where the system can find it. There is also a small dlltest program to test the dll. Run with: dlltest phototest.tif phototest.txt It will output the text from phototest.tif with bounding box information. **New for Windows** the distribution now includes tesseract.exe and tessdll.dll which *might* work out of the box! There are no guarantees as you need VC++6 versions of mfc and crt (at least) for it to work. (Batteries not included, and certainly no installshield.) == Important note for anyone building with make: i.e. anyone except devstudio users == This release includes new standardization for the data directory. To enable Tesseract to find its data files, you must either: ./configure make make install to move the data files to the standard place, or: export TESSDATA_PREFIX="directory in which your tessdata resides/" (or equivalent) in your .profile or whatever or setenv to set the environment variable. Note that the directory must end in a / HAVING tesseract and tessdata IN THE SAME DIRECTORY DOES NOT WORK ANY MORE. == All users == Fixed a bunch of name collisions - mostly with stl. Made some preliminary changes for unicode compatibility. Includes a new data file (unicharset) and renaming of the other data files to eng.* to support different languages. There are also several other minor bug fixes and portability improvements for 64 bit, the latest visual studio compiler etc. Thanks to all who have contributed these fixes. NOTE: This is likely to be the last English-only release! Apologies in advance to non-windows users for bloating the distribution with windows executables. This will probably get fixed in the next release with the multi-language capability, since that will also bloat the distribution. Tesseract release notes Feb 2, 2007 - V1.03. Added mftraining and cntraining. Using an image with a box file, tesseract generates .tr output files. cntraining runs on the .tr files to make normproto that lives in tessdata. mftraining runs on the .tr files to make inttemp and pffmtable in tessdata. These are the main data files that tesseract uses to recognize characters. At present, the code to make dictionary files is not yet available, nor are any sample box files or rebuilt inttemp or documentation to create any of these. Recognition is still limited to the ASCII set, but when this problem is fixed, documentation will follow. Added a new API with adaptive thresholding for grey and color images. See ccmain/baseapi.h/cpp for details. The main program has been converted to use the API as an example. See main() in ccmain/tesseractmain.cpp for details. The API is designed to make it easy to add subclasses with ability to output the bounding boxes etc from the internal structures. The adaptive thresholding improves accuracy (most of the time) on non-binary images. Many memory leaks have been fixed. There are no known leaks left from using the API correctly. The adaptive classifier was not operating correctly. This bug, and several others have been fixed, including poor chopping, an indefinite (if not quite infinite) loop in the number parser, and a couple of crash bugs. Thanks to all that have contributed bugs and bug fixes. It is now possible to build without any of the graphics support to save code size using #define GRAPHICS_DISABLED. There is also a new EMBEDDED define for use on operating systems with limited library support. 64-bit and Mac OSX buildability is now included in the mainline source tree. Thanks to all that have contributed patches and comments to help with that. 1.03 is also endian-independent, apart from the tiff i/o, so if you use libtiff, the code should run on all platforms, even if you get/create new data files of a different endinanness. Some of the bug fixes improve accuracy, and so do some of the changes to DangAmbigs and user-words. Tesseract release notes, Oct 4 2006 - V1.02. Removed dependency on aspirin. *All* code is now licensed under Apache2.0. Tesseract release notes, Sep 7 2006 - V1.01. Fixes for this release: Added mfcpch.cpp and getopt.cpp for VC++. Fixed problem with greyscale images and no libtiff. Stopped debug window from being used for the usage output. Fixed load of inttemp for big-endian architectures. Fixed some Mac compilation issues. This version should read uncompressed 8 bit grey and 24 bit color tiffs without having to have libtiff. It does a dumb threshold though, so don't expect good results from poor contrast or images of natural scenes etc. If you just run tesseract with no command line args you should now get a sensible usage message on linux, with or without X-windows. If you can get it to compile on a PPC Mac, it may now run correctly, although not all the build issues are fixed yet. Building Tesseract: Windows: Unpack the tar.gz archive Open tesseract.dsw in DevStudio (preferably version 6, higher versions will be more difficult) Set Win32 - Release as the active configuration. Build. Copy tesseract.exe from bin.rel up one directory level. Run tesseract phototest.tif phototest This will create phototest.txt. Linux: Unpack the tar.gz archive ./configure make Copy tesseract from ccmain up one directory level (or create a symbolic link) Run tesseract phototest.tif phototest This will create phototest.txt. tesseract-3.04.01/android/000077500000000000000000000000001266071204500152775ustar00rootroot00000000000000tesseract-3.04.01/android/AndroidManifest.xml000066400000000000000000000001711266071204500210670ustar00rootroot00000000000000tesseract-3.04.01/android/Makefile.am000066400000000000000000000001031266071204500173250ustar00rootroot00000000000000EXTRA_DIST = AndroidManifest.xml jni/Android.mk jni/Application.mk tesseract-3.04.01/android/jni/000077500000000000000000000000001266071204500160575ustar00rootroot00000000000000tesseract-3.04.01/android/jni/Android.mk000066400000000000000000000040741266071204500177750ustar00rootroot00000000000000LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := tesseract-$(APP_ABI) LOCAL_STATIC_LIBRARIES := \ mobile_base \ leptonica-$(APP_ABI) LOCAL_C_INCLUDES := $(APP_C_INCLUDES) LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/../../api \ $(LOCAL_PATH)/../../ccmain\ $(LOCAL_PATH)/../../ccstruct\ $(LOCAL_PATH)/../../ccutil\ $(LOCAL_PATH)/../../classify\ $(LOCAL_PATH)/../../cutil\ $(LOCAL_PATH)/../../dict\ $(LOCAL_PATH)/../../image\ $(LOCAL_PATH)/../../textord\ $(LOCAL_PATH)/../../third_party\ $(LOCAL_PATH)/../../wordrec\ $(LOCAL_PATH)/../../opencl\ $(LOCAL_PATH)/../../viewer\ $(LOCAL_PATH)/../../../leptonica/include $(info local c includes=$(LOCAL_C_INCLUDES)) $(info local path=$(LOCAL_PATH)) LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/../../api/*.cpp $(LOCAL_PATH)/../../ccmain/*.cpp $(LOCAL_PATH)/../../ccstruct/*.cpp $(LOCAL_PATH)/../../ccutil/*.cpp $(LOCAL_PATH)/../../classify/*.cpp $(LOCAL_PATH)/../../cutil/*.cpp $(LOCAL_PATH)/../../dict/*.cpp $(LOCAL_PATH)/../../image/*.cpp $(LOCAL_PATH)/../../textord/*.cpp $(LOCAL_PATH)/../../viewer/*.cpp $(LOCAL_PATH)/../../wordrec/*.cpp) EXPLICIT_SRC_EXCLUDES := \ $(LOCAL_PATH)/../../ccmain/cubeclassifier.cpp \ $(LOCAL_PATH)/../../ccmain/cubeclassifier.h \ $(LOCAL_PATH)/../../ccmain/cube_control.cpp \ $(LOCAL_PATH)/../../ccmain/cube_reco_context.cpp \ $(LOCAL_PATH)/../../ccmain/cube_reco_context.h \ $(LOCAL_PATH)/../../ccmain/tesseract_cube_combiner.cpp \ $(LOCAL_PATH)/../../ccmain/tesseract_cube_combiner.h \ $(LOCAL_PATH)/../../api/pdfrenderer.cpp \ $(LOCAL_PATH)/../../api/tesseractmain.cpp \ LOCAL_SRC_FILES := $(filter-out $(EXPLICIT_SRC_EXCLUDES), $(LOCAL_SRC_FILES)) LOCAL_SRC_FILES := $(LOCAL_SRC_FILES:$(LOCAL_PATH)/%=%) $(info local src files = $(LOCAL_SRC_FILES)) LOCAL_LDLIBS := -ldl -llog -ljnigraphics LOCAL_CFLAGS := -DANDROID_BUILD -DNO_CUBE_BUILD -DGRAPHICS_DISABLED include $(BUILD_SHARED_LIBRARY) $(call import-module,mobile/base) $(call import-module,mobile/base) $(call import-module,mobile/util/hash) $(call import-module,third_party/leptonica/android/jni) tesseract-3.04.01/android/jni/Application.mk000066400000000000000000000010701266071204500206510ustar00rootroot00000000000000# Include common.mk for building google3 native code. DEPOT_PATH := $(firstword $(subst /google3, ,$(abspath $(call my-dir)))) ifneq ($(wildcard $(DEPOT_PATH)/google3/mobile/build/common.mk),) include $(DEPOT_PATH)/google3/mobile/build/common.mk else include $(DEPOT_PATH)/READONLY/google3/mobile/build/common.mk endif # Specify the hash namespace that we're using, based on the APP_STL we're using. APP_CFLAGS += -Werror -DHASH_NAMESPACE=__gnu_cxx -Wno-error=deprecated-register APP_PLATFORM := android-16 APP_STL := gnustl_static NDK_TOOLCHAIN_VERSION := clang tesseract-3.04.01/api/000077500000000000000000000000001266071204500144305ustar00rootroot00000000000000tesseract-3.04.01/api/Makefile.am000066400000000000000000000055331266071204500164720ustar00rootroot00000000000000AM_CPPFLAGS += -DLOCALEDIR=\"$(localedir)\"\ -DUSE_STD_NAMESPACE \ -I$(top_srcdir)/ccutil -I$(top_srcdir)/ccstruct -I$(top_srcdir)/cube \ -I$(top_srcdir)/viewer \ -I$(top_srcdir)/textord -I$(top_srcdir)/dict \ -I$(top_srcdir)/classify -I$(top_srcdir)/ccmain \ -I$(top_srcdir)/wordrec -I$(top_srcdir)/cutil \ -I$(top_srcdir)/opencl AM_CPPFLAGS += $(OPENCL_CPPFLAGS) if VISIBILITY AM_CPPFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden endif include_HEADERS = apitypes.h baseapi.h capi.h renderer.h lib_LTLIBRARIES = if !USING_MULTIPLELIBS noinst_LTLIBRARIES = libtesseract_api.la else lib_LTLIBRARIES += libtesseract_api.la libtesseract_api_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) libtesseract_api_la_LIBADD = \ ../ccmain/libtesseract_main.la \ ../textord/libtesseract_textord.la \ ../wordrec/libtesseract_wordrec.la \ ../classify/libtesseract_classify.la \ ../dict/libtesseract_dict.la \ ../ccstruct/libtesseract_ccstruct.la \ ../cutil/libtesseract_cutil.la \ ../viewer/libtesseract_viewer.la \ ../ccutil/libtesseract_ccutil.la \ ../opencl/libtesseract_opencl.la if !NO_CUBE_BUILD libtesseract_api_la_LIBADD += ../cube/libtesseract_cube.la \ ../neural_networks/runtime/libtesseract_neural.la \ endif endif libtesseract_api_la_CPPFLAGS = $(AM_CPPFLAGS) if VISIBILITY libtesseract_api_la_CPPFLAGS += -DTESS_EXPORTS endif libtesseract_api_la_SOURCES = baseapi.cpp capi.cpp renderer.cpp pdfrenderer.cpp lib_LTLIBRARIES += libtesseract.la libtesseract_la_LDFLAGS = libtesseract_la_SOURCES = # Dummy C++ source to cause C++ linking. # see http://www.gnu.org/s/hello/manual/automake/Libtool-Convenience-Libraries.html#Libtool-Convenience-Libraries nodist_EXTRA_libtesseract_la_SOURCES = dummy.cxx libtesseract_la_LIBADD = \ libtesseract_api.la \ ../ccmain/libtesseract_main.la \ ../textord/libtesseract_textord.la \ ../wordrec/libtesseract_wordrec.la \ ../classify/libtesseract_classify.la \ ../dict/libtesseract_dict.la \ ../ccstruct/libtesseract_ccstruct.la \ ../cutil/libtesseract_cutil.la \ ../viewer/libtesseract_viewer.la \ ../ccutil/libtesseract_ccutil.la \ ../opencl/libtesseract_opencl.la if !NO_CUBE_BUILD libtesseract_la_LIBADD += ../cube/libtesseract_cube.la \ ../neural_networks/runtime/libtesseract_neural.la endif libtesseract_la_LDFLAGS += -version-info $(GENERIC_LIBRARY_VERSION) bin_PROGRAMS = tesseract tesseract_SOURCES = tesseractmain.cpp tesseract_CPPFLAGS = $(AM_CPPFLAGS) if VISIBILITY tesseract_CPPFLAGS += -DTESS_IMPORTS endif tesseract_LDADD = libtesseract.la tesseract_LDFLAGS = $(OPENCL_LDFLAGS) if OPENMP tesseract_LDADD += $(OPENMP_CFLAGS) endif if T_WIN tesseract_LDADD += -lws2_32 libtesseract_la_LDFLAGS += -no-undefined -Wl,--as-needed -lws2_32 endif if ADD_RT tesseract_LDADD += -lrt endif tesseract-3.04.01/api/Makefile.in000066400000000000000000001136501266071204500165030ustar00rootroot00000000000000# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @VISIBILITY_TRUE@am__append_1 = -fvisibility=hidden -fvisibility-inlines-hidden @USING_MULTIPLELIBS_TRUE@am__append_2 = libtesseract_api.la @VISIBILITY_TRUE@am__append_3 = -DTESS_EXPORTS @NO_CUBE_BUILD_FALSE@am__append_4 = ../cube/libtesseract_cube.la \ @NO_CUBE_BUILD_FALSE@ ../neural_networks/runtime/libtesseract_neural.la bin_PROGRAMS = tesseract$(EXEEXT) @VISIBILITY_TRUE@am__append_5 = -DTESS_IMPORTS @OPENMP_TRUE@am__append_6 = $(OPENMP_CFLAGS) @T_WIN_TRUE@am__append_7 = -lws2_32 @T_WIN_TRUE@am__append_8 = -no-undefined -Wl,--as-needed -lws2_32 @ADD_RT_TRUE@am__append_9 = -lrt subdir = api DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/config/depcomp $(include_HEADERS) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config_auto.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ "$(DESTDIR)$(includedir)" LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) libtesseract_la_DEPENDENCIES = libtesseract_api.la \ ../ccmain/libtesseract_main.la \ ../textord/libtesseract_textord.la \ ../wordrec/libtesseract_wordrec.la \ ../classify/libtesseract_classify.la \ ../dict/libtesseract_dict.la \ ../ccstruct/libtesseract_ccstruct.la \ ../cutil/libtesseract_cutil.la \ ../viewer/libtesseract_viewer.la \ ../ccutil/libtesseract_ccutil.la \ ../opencl/libtesseract_opencl.la $(am__append_4) am_libtesseract_la_OBJECTS = libtesseract_la_OBJECTS = $(am_libtesseract_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libtesseract_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ $(AM_CXXFLAGS) $(CXXFLAGS) $(libtesseract_la_LDFLAGS) \ $(LDFLAGS) -o $@ @USING_MULTIPLELIBS_TRUE@libtesseract_api_la_DEPENDENCIES = \ @USING_MULTIPLELIBS_TRUE@ ../ccmain/libtesseract_main.la \ @USING_MULTIPLELIBS_TRUE@ ../textord/libtesseract_textord.la \ @USING_MULTIPLELIBS_TRUE@ ../wordrec/libtesseract_wordrec.la \ @USING_MULTIPLELIBS_TRUE@ ../classify/libtesseract_classify.la \ @USING_MULTIPLELIBS_TRUE@ ../dict/libtesseract_dict.la \ @USING_MULTIPLELIBS_TRUE@ ../ccstruct/libtesseract_ccstruct.la \ @USING_MULTIPLELIBS_TRUE@ ../cutil/libtesseract_cutil.la \ @USING_MULTIPLELIBS_TRUE@ ../viewer/libtesseract_viewer.la \ @USING_MULTIPLELIBS_TRUE@ ../ccutil/libtesseract_ccutil.la \ @USING_MULTIPLELIBS_TRUE@ ../opencl/libtesseract_opencl.la \ @USING_MULTIPLELIBS_TRUE@ ../cube/libtesseract_cube.la \ @USING_MULTIPLELIBS_TRUE@ ../neural_networks/runtime/libtesseract_neural.la \ @USING_MULTIPLELIBS_TRUE@ endif am_libtesseract_api_la_OBJECTS = libtesseract_api_la-baseapi.lo \ libtesseract_api_la-capi.lo libtesseract_api_la-renderer.lo \ libtesseract_api_la-pdfrenderer.lo libtesseract_api_la_OBJECTS = $(am_libtesseract_api_la_OBJECTS) libtesseract_api_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ $(AM_CXXFLAGS) $(CXXFLAGS) $(libtesseract_api_la_LDFLAGS) \ $(LDFLAGS) -o $@ @USING_MULTIPLELIBS_FALSE@am_libtesseract_api_la_rpath = @USING_MULTIPLELIBS_TRUE@am_libtesseract_api_la_rpath = -rpath \ @USING_MULTIPLELIBS_TRUE@ $(libdir) PROGRAMS = $(bin_PROGRAMS) am_tesseract_OBJECTS = tesseract-tesseractmain.$(OBJEXT) tesseract_OBJECTS = $(am_tesseract_OBJECTS) am__DEPENDENCIES_1 = tesseract_DEPENDENCIES = libtesseract.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) tesseract_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(tesseract_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__depfiles_maybe = depfiles am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(libtesseract_la_SOURCES) \ $(nodist_EXTRA_libtesseract_la_SOURCES) \ $(libtesseract_api_la_SOURCES) $(tesseract_SOURCES) DIST_SOURCES = $(libtesseract_la_SOURCES) \ $(libtesseract_api_la_SOURCES) $(tesseract_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(include_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_CPPFLAGS = @AM_CPPFLAGS@ -DLOCALEDIR=\"$(localedir)\" \ -DUSE_STD_NAMESPACE -I$(top_srcdir)/ccutil \ -I$(top_srcdir)/ccstruct -I$(top_srcdir)/cube \ -I$(top_srcdir)/viewer -I$(top_srcdir)/textord \ -I$(top_srcdir)/dict -I$(top_srcdir)/classify \ -I$(top_srcdir)/ccmain -I$(top_srcdir)/wordrec \ -I$(top_srcdir)/cutil -I$(top_srcdir)/opencl \ $(OPENCL_CPPFLAGS) $(am__append_1) AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AM_LDFLAGS = @AM_LDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FRAMEWORK_OPENCL = @FRAMEWORK_OPENCL@ GENERIC_API_VERSION = @GENERIC_API_VERSION@ GENERIC_LIBRARY_NAME = @GENERIC_LIBRARY_NAME@ GENERIC_LIBRARY_VERSION = @GENERIC_LIBRARY_VERSION@ GENERIC_MAJOR_VERSION = @GENERIC_MAJOR_VERSION@ GENERIC_RELEASE = @GENERIC_RELEASE@ GENERIC_VERSION = @GENERIC_VERSION@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBLEPT_HEADERSDIR = @LIBLEPT_HEADERSDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCL_CPPFLAGS = @OPENCL_CPPFLAGS@ OPENCL_LDFLAGS = @OPENCL_LDFLAGS@ OPENMP_CXXFLAGS = @OPENMP_CXXFLAGS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_DATE = @PACKAGE_DATE@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_YEAR = @PACKAGE_YEAR@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ include_HEADERS = apitypes.h baseapi.h capi.h renderer.h lib_LTLIBRARIES = $(am__append_2) libtesseract.la @USING_MULTIPLELIBS_FALSE@noinst_LTLIBRARIES = libtesseract_api.la @USING_MULTIPLELIBS_TRUE@libtesseract_api_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) @USING_MULTIPLELIBS_TRUE@libtesseract_api_la_LIBADD = \ @USING_MULTIPLELIBS_TRUE@ ../ccmain/libtesseract_main.la \ @USING_MULTIPLELIBS_TRUE@ ../textord/libtesseract_textord.la \ @USING_MULTIPLELIBS_TRUE@ ../wordrec/libtesseract_wordrec.la \ @USING_MULTIPLELIBS_TRUE@ ../classify/libtesseract_classify.la \ @USING_MULTIPLELIBS_TRUE@ ../dict/libtesseract_dict.la \ @USING_MULTIPLELIBS_TRUE@ ../ccstruct/libtesseract_ccstruct.la \ @USING_MULTIPLELIBS_TRUE@ ../cutil/libtesseract_cutil.la \ @USING_MULTIPLELIBS_TRUE@ ../viewer/libtesseract_viewer.la \ @USING_MULTIPLELIBS_TRUE@ ../ccutil/libtesseract_ccutil.la \ @USING_MULTIPLELIBS_TRUE@ ../opencl/libtesseract_opencl.la \ @USING_MULTIPLELIBS_TRUE@ ../cube/libtesseract_cube.la \ @USING_MULTIPLELIBS_TRUE@ ../neural_networks/runtime/libtesseract_neural.la \ @USING_MULTIPLELIBS_TRUE@ endif libtesseract_api_la_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_3) libtesseract_api_la_SOURCES = baseapi.cpp capi.cpp renderer.cpp pdfrenderer.cpp libtesseract_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) \ $(am__append_8) libtesseract_la_SOURCES = # Dummy C++ source to cause C++ linking. # see http://www.gnu.org/s/hello/manual/automake/Libtool-Convenience-Libraries.html#Libtool-Convenience-Libraries nodist_EXTRA_libtesseract_la_SOURCES = dummy.cxx libtesseract_la_LIBADD = libtesseract_api.la \ ../ccmain/libtesseract_main.la \ ../textord/libtesseract_textord.la \ ../wordrec/libtesseract_wordrec.la \ ../classify/libtesseract_classify.la \ ../dict/libtesseract_dict.la \ ../ccstruct/libtesseract_ccstruct.la \ ../cutil/libtesseract_cutil.la \ ../viewer/libtesseract_viewer.la \ ../ccutil/libtesseract_ccutil.la \ ../opencl/libtesseract_opencl.la $(am__append_4) tesseract_SOURCES = tesseractmain.cpp tesseract_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_5) tesseract_LDADD = libtesseract.la $(am__append_6) $(am__append_7) \ $(am__append_9) tesseract_LDFLAGS = $(OPENCL_LDFLAGS) all: all-am .SUFFIXES: .SUFFIXES: .cpp .cxx .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign api/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign api/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libtesseract.la: $(libtesseract_la_OBJECTS) $(libtesseract_la_DEPENDENCIES) $(EXTRA_libtesseract_la_DEPENDENCIES) $(AM_V_CXXLD)$(libtesseract_la_LINK) -rpath $(libdir) $(libtesseract_la_OBJECTS) $(libtesseract_la_LIBADD) $(LIBS) libtesseract_api.la: $(libtesseract_api_la_OBJECTS) $(libtesseract_api_la_DEPENDENCIES) $(EXTRA_libtesseract_api_la_DEPENDENCIES) $(AM_V_CXXLD)$(libtesseract_api_la_LINK) $(am_libtesseract_api_la_rpath) $(libtesseract_api_la_OBJECTS) $(libtesseract_api_la_LIBADD) $(LIBS) install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list tesseract$(EXEEXT): $(tesseract_OBJECTS) $(tesseract_DEPENDENCIES) $(EXTRA_tesseract_DEPENDENCIES) @rm -f tesseract$(EXEEXT) $(AM_V_CXXLD)$(tesseract_LINK) $(tesseract_OBJECTS) $(tesseract_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dummy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtesseract_api_la-baseapi.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtesseract_api_la-capi.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtesseract_api_la-pdfrenderer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtesseract_api_la-renderer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tesseract-tesseractmain.Po@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< libtesseract_api_la-baseapi.lo: baseapi.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtesseract_api_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libtesseract_api_la-baseapi.lo -MD -MP -MF $(DEPDIR)/libtesseract_api_la-baseapi.Tpo -c -o libtesseract_api_la-baseapi.lo `test -f 'baseapi.cpp' || echo '$(srcdir)/'`baseapi.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libtesseract_api_la-baseapi.Tpo $(DEPDIR)/libtesseract_api_la-baseapi.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='baseapi.cpp' object='libtesseract_api_la-baseapi.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtesseract_api_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libtesseract_api_la-baseapi.lo `test -f 'baseapi.cpp' || echo '$(srcdir)/'`baseapi.cpp libtesseract_api_la-capi.lo: capi.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtesseract_api_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libtesseract_api_la-capi.lo -MD -MP -MF $(DEPDIR)/libtesseract_api_la-capi.Tpo -c -o libtesseract_api_la-capi.lo `test -f 'capi.cpp' || echo '$(srcdir)/'`capi.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libtesseract_api_la-capi.Tpo $(DEPDIR)/libtesseract_api_la-capi.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='capi.cpp' object='libtesseract_api_la-capi.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtesseract_api_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libtesseract_api_la-capi.lo `test -f 'capi.cpp' || echo '$(srcdir)/'`capi.cpp libtesseract_api_la-renderer.lo: renderer.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtesseract_api_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libtesseract_api_la-renderer.lo -MD -MP -MF $(DEPDIR)/libtesseract_api_la-renderer.Tpo -c -o libtesseract_api_la-renderer.lo `test -f 'renderer.cpp' || echo '$(srcdir)/'`renderer.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libtesseract_api_la-renderer.Tpo $(DEPDIR)/libtesseract_api_la-renderer.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='renderer.cpp' object='libtesseract_api_la-renderer.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtesseract_api_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libtesseract_api_la-renderer.lo `test -f 'renderer.cpp' || echo '$(srcdir)/'`renderer.cpp libtesseract_api_la-pdfrenderer.lo: pdfrenderer.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtesseract_api_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libtesseract_api_la-pdfrenderer.lo -MD -MP -MF $(DEPDIR)/libtesseract_api_la-pdfrenderer.Tpo -c -o libtesseract_api_la-pdfrenderer.lo `test -f 'pdfrenderer.cpp' || echo '$(srcdir)/'`pdfrenderer.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libtesseract_api_la-pdfrenderer.Tpo $(DEPDIR)/libtesseract_api_la-pdfrenderer.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='pdfrenderer.cpp' object='libtesseract_api_la-pdfrenderer.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtesseract_api_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libtesseract_api_la-pdfrenderer.lo `test -f 'pdfrenderer.cpp' || echo '$(srcdir)/'`pdfrenderer.cpp tesseract-tesseractmain.o: tesseractmain.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tesseract_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT tesseract-tesseractmain.o -MD -MP -MF $(DEPDIR)/tesseract-tesseractmain.Tpo -c -o tesseract-tesseractmain.o `test -f 'tesseractmain.cpp' || echo '$(srcdir)/'`tesseractmain.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tesseract-tesseractmain.Tpo $(DEPDIR)/tesseract-tesseractmain.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tesseractmain.cpp' object='tesseract-tesseractmain.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tesseract_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o tesseract-tesseractmain.o `test -f 'tesseractmain.cpp' || echo '$(srcdir)/'`tesseractmain.cpp tesseract-tesseractmain.obj: tesseractmain.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tesseract_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT tesseract-tesseractmain.obj -MD -MP -MF $(DEPDIR)/tesseract-tesseractmain.Tpo -c -o tesseract-tesseractmain.obj `if test -f 'tesseractmain.cpp'; then $(CYGPATH_W) 'tesseractmain.cpp'; else $(CYGPATH_W) '$(srcdir)/tesseractmain.cpp'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tesseract-tesseractmain.Tpo $(DEPDIR)/tesseract-tesseractmain.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tesseractmain.cpp' object='tesseract-tesseractmain.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tesseract_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o tesseract-tesseractmain.obj `if test -f 'tesseractmain.cpp'; then $(CYGPATH_W) 'tesseractmain.cpp'; else $(CYGPATH_W) '$(srcdir)/tesseractmain.cpp'; fi` .cxx.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cxx.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cxx.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-includeHEADERS: $(include_HEADERS) @$(NORMAL_INSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ done uninstall-includeHEADERS: @$(NORMAL_UNINSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) install-binPROGRAMS: install-libLTLIBRARIES installdirs: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(includedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ clean-libtool clean-noinstLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-includeHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-libLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \ uninstall-libLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-binPROGRAMS \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-includeHEADERS install-info install-info-am \ install-libLTLIBRARIES install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \ uninstall-includeHEADERS uninstall-libLTLIBRARIES @USING_MULTIPLELIBS_TRUE@ if !NO_CUBE_BUILD # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: tesseract-3.04.01/api/apitypes.h000066400000000000000000000025171266071204500164440ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: apitypes.h // Description: Types used in both the API and internally // Author: Ray Smith // Created: Wed Mar 03 09:22:53 PST 2010 // // (C) Copyright 2010, 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_API_APITYPES_H__ #define TESSERACT_API_APITYPES_H__ #include "publictypes.h" // The types used by the API and Page/ResultIterator can be found in: // ccstruct/publictypes.h // ccmain/resultiterator.h // ccmain/pageiterator.h // API interfaces and API users should be sure to include this file, rather // than the lower-level one, and lower-level code should be sure to include // only the lower-level file. #endif // TESSERACT_API_APITYPES_H__ tesseract-3.04.01/api/baseapi.cpp000066400000000000000000002650021266071204500165450ustar00rootroot00000000000000/********************************************************************** * File: baseapi.cpp * Description: Simple API for calling tesseract. * Author: Ray Smith * Created: Fri Oct 06 15:35:01 PDT 2006 * * (C) Copyright 2006, 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 automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #ifdef __linux__ #include #endif #if defined(_WIN32) #ifdef _MSC_VER #include "vcsversion.h" #include "mathfix.h" #elif MINGW // workaround for stdlib.h with -std=c++11 for _splitpath and _MAX_FNAME #undef __STRICT_ANSI__ #endif // _MSC_VER #include #include #include #include #else #include #include #include #endif // _WIN32 #include #include #include #include #include "allheaders.h" #include "baseapi.h" #include "blobclass.h" #include "resultiterator.h" #include "mutableiterator.h" #include "thresholder.h" #include "tesseractclass.h" #include "pageres.h" #include "paragraphs.h" #include "tessvars.h" #include "control.h" #include "dict.h" #include "pgedit.h" #include "paramsd.h" #include "output.h" #include "globaloc.h" #include "globals.h" #include "edgblob.h" #include "equationdetect.h" #include "tessbox.h" #include "makerow.h" #include "otsuthr.h" #include "osdetect.h" #include "params.h" #include "renderer.h" #include "strngs.h" #include "openclwrapper.h" BOOL_VAR(stream_filelist, FALSE, "Stream a filelist from stdin"); namespace tesseract { /** Minimum sensible image size to be worth running tesseract. */ const int kMinRectSize = 10; /** Character returned when Tesseract couldn't recognize as anything. */ const char kTesseractReject = '~'; /** Character used by UNLV error counter as a reject. */ const char kUNLVReject = '~'; /** Character used by UNLV as a suspect marker. */ const char kUNLVSuspect = '^'; /** * Filename used for input image file, from which to derive a name to search * for a possible UNLV zone file, if none is specified by SetInputName. */ const char* kInputFile = "noname.tif"; /** * Temp file used for storing current parameters before applying retry values. */ const char* kOldVarsFile = "failed_vars.txt"; /** Max string length of an int. */ const int kMaxIntSize = 22; /** * Minimum believable resolution. Used as a default if there is no other * information, as it is safer to under-estimate than over-estimate. */ const int kMinCredibleResolution = 70; /** Maximum believable resolution. */ const int kMaxCredibleResolution = 2400; TessBaseAPI::TessBaseAPI() : tesseract_(NULL), osd_tesseract_(NULL), equ_detect_(NULL), // Thresholder is initialized to NULL here, but will be set before use by: // A constructor of a derived API, SetThresholder(), or // created implicitly when used in InternalSetImage. thresholder_(NULL), paragraph_models_(NULL), block_list_(NULL), page_res_(NULL), input_file_(NULL), input_image_(NULL), output_file_(NULL), datapath_(NULL), language_(NULL), last_oem_requested_(OEM_DEFAULT), recognition_done_(false), truth_cb_(NULL), rect_left_(0), rect_top_(0), rect_width_(0), rect_height_(0), image_width_(0), image_height_(0) { } TessBaseAPI::~TessBaseAPI() { End(); } /** * Returns the version identifier as a static string. Do not delete. */ const char* TessBaseAPI::Version() { #if defined(GIT_REV) && (defined(DEBUG) || defined(_DEBUG)) return GIT_REV; #else return TESSERACT_VERSION_STR; #endif } /** * If compiled with OpenCL AND an available OpenCL * device is deemed faster than serial code, then * "device" is populated with the cl_device_id * and returns sizeof(cl_device_id) * otherwise *device=NULL and returns 0. */ #ifdef USE_OPENCL #if USE_DEVICE_SELECTION #include "opencl_device_selection.h" #endif #endif size_t TessBaseAPI::getOpenCLDevice(void **data) { #ifdef USE_OPENCL #if USE_DEVICE_SELECTION ds_device device = OpenclDevice::getDeviceSelection(); if (device.type == DS_DEVICE_OPENCL_DEVICE) { *data = reinterpret_cast(new cl_device_id); memcpy(*data, &device.oclDeviceID, sizeof(cl_device_id)); return sizeof(cl_device_id); } #endif #endif *data = NULL; return 0; } /** * Writes the thresholded image to stderr as a PBM file on receipt of a * SIGSEGV, SIGFPE, or SIGBUS signal. (Linux/Unix only). */ void TessBaseAPI::CatchSignals() { #ifdef __linux__ struct sigaction action; memset(&action, 0, sizeof(action)); action.sa_handler = &signal_exit; action.sa_flags = SA_RESETHAND; sigaction(SIGSEGV, &action, NULL); sigaction(SIGFPE, &action, NULL); sigaction(SIGBUS, &action, NULL); #else // Warn API users that an implementation is needed. tprintf("CatchSignals has no non-linux implementation!\n"); #endif } /** * Set the name of the input file. Needed only for training and * loading a UNLV zone file. */ void TessBaseAPI::SetInputName(const char* name) { if (input_file_ == NULL) input_file_ = new STRING(name); else *input_file_ = name; } /** Set the name of the output files. Needed only for debugging. */ void TessBaseAPI::SetOutputName(const char* name) { if (output_file_ == NULL) output_file_ = new STRING(name); else *output_file_ = name; } bool TessBaseAPI::SetVariable(const char* name, const char* value) { if (tesseract_ == NULL) tesseract_ = new Tesseract; return ParamUtils::SetParam(name, value, SET_PARAM_CONSTRAINT_NON_INIT_ONLY, tesseract_->params()); } bool TessBaseAPI::SetDebugVariable(const char* name, const char* value) { if (tesseract_ == NULL) tesseract_ = new Tesseract; return ParamUtils::SetParam(name, value, SET_PARAM_CONSTRAINT_DEBUG_ONLY, tesseract_->params()); } bool TessBaseAPI::GetIntVariable(const char *name, int *value) const { IntParam *p = ParamUtils::FindParam( name, GlobalParams()->int_params, tesseract_->params()->int_params); if (p == NULL) return false; *value = (inT32)(*p); return true; } bool TessBaseAPI::GetBoolVariable(const char *name, bool *value) const { BoolParam *p = ParamUtils::FindParam( name, GlobalParams()->bool_params, tesseract_->params()->bool_params); if (p == NULL) return false; *value = (BOOL8)(*p); return true; } const char *TessBaseAPI::GetStringVariable(const char *name) const { StringParam *p = ParamUtils::FindParam( name, GlobalParams()->string_params, tesseract_->params()->string_params); return (p != NULL) ? p->string() : NULL; } bool TessBaseAPI::GetDoubleVariable(const char *name, double *value) const { DoubleParam *p = ParamUtils::FindParam( name, GlobalParams()->double_params, tesseract_->params()->double_params); if (p == NULL) return false; *value = (double)(*p); return true; } /** Get value of named variable as a string, if it exists. */ bool TessBaseAPI::GetVariableAsString(const char *name, STRING *val) { return ParamUtils::GetParamAsString(name, tesseract_->params(), val); } /** Print Tesseract parameters to the given file. */ void TessBaseAPI::PrintVariables(FILE *fp) const { ParamUtils::PrintParams(fp, tesseract_->params()); } /** * The datapath must be the name of the data directory (no ending /) or * some other file in which the data directory resides (for instance argv[0].) * The language is (usually) an ISO 639-3 string or NULL will default to eng. * If numeric_mode is true, then only digits and Roman numerals will * be returned. * @return: 0 on success and -1 on initialization failure. */ int TessBaseAPI::Init(const char* datapath, const char* language, OcrEngineMode oem, char **configs, int configs_size, const GenericVector *vars_vec, const GenericVector *vars_values, bool set_only_non_debug_params) { PERF_COUNT_START("TessBaseAPI::Init") // Default language is "eng". if (language == NULL) language = "eng"; // If the datapath, OcrEngineMode or the language have changed - start again. // Note that the language_ field stores the last requested language that was // initialized successfully, while tesseract_->lang stores the language // actually used. They differ only if the requested language was NULL, in // which case tesseract_->lang is set to the Tesseract default ("eng"). if (tesseract_ != NULL && (datapath_ == NULL || language_ == NULL || *datapath_ != datapath || last_oem_requested_ != oem || (*language_ != language && tesseract_->lang != language))) { delete tesseract_; tesseract_ = NULL; } // PERF_COUNT_SUB("delete tesseract_") #ifdef USE_OPENCL OpenclDevice od; od.InitEnv(); #endif PERF_COUNT_SUB("OD::InitEnv()") bool reset_classifier = true; if (tesseract_ == NULL) { reset_classifier = false; tesseract_ = new Tesseract; if (tesseract_->init_tesseract( datapath, output_file_ != NULL ? output_file_->string() : NULL, language, oem, configs, configs_size, vars_vec, vars_values, set_only_non_debug_params) != 0) { return -1; } } PERF_COUNT_SUB("update tesseract_") // Update datapath and language requested for the last valid initialization. if (datapath_ == NULL) datapath_ = new STRING(datapath); else *datapath_ = datapath; if ((strcmp(datapath_->string(), "") == 0) && (strcmp(tesseract_->datadir.string(), "") != 0)) *datapath_ = tesseract_->datadir; if (language_ == NULL) language_ = new STRING(language); else *language_ = language; last_oem_requested_ = oem; // PERF_COUNT_SUB("update last_oem_requested_") // For same language and datapath, just reset the adaptive classifier. if (reset_classifier) { tesseract_->ResetAdaptiveClassifier(); PERF_COUNT_SUB("tesseract_->ResetAdaptiveClassifier()") } PERF_COUNT_END return 0; } /** * Returns the languages string used in the last valid initialization. * If the last initialization specified "deu+hin" then that will be * returned. If hin loaded eng automatically as well, then that will * not be included in this list. To find the languages actually * loaded use GetLoadedLanguagesAsVector. * The returned string should NOT be deleted. */ const char* TessBaseAPI::GetInitLanguagesAsString() const { return (language_ == NULL || language_->string() == NULL) ? "" : language_->string(); } /** * Returns the loaded languages in the vector of STRINGs. * Includes all languages loaded by the last Init, including those loaded * as dependencies of other loaded languages. */ void TessBaseAPI::GetLoadedLanguagesAsVector( GenericVector* langs) const { langs->clear(); if (tesseract_ != NULL) { langs->push_back(tesseract_->lang); int num_subs = tesseract_->num_sub_langs(); for (int i = 0; i < num_subs; ++i) langs->push_back(tesseract_->get_sub_lang(i)->lang); } } /** * Returns the available languages in the vector of STRINGs. */ void TessBaseAPI::GetAvailableLanguagesAsVector( GenericVector* langs) const { langs->clear(); if (tesseract_ != NULL) { #ifdef _WIN32 STRING pattern = tesseract_->datadir + "/*." + kTrainedDataSuffix; char fname[_MAX_FNAME]; WIN32_FIND_DATA data; BOOL result = TRUE; HANDLE handle = FindFirstFile(pattern.string(), &data); if (handle != INVALID_HANDLE_VALUE) { for (; result; result = FindNextFile(handle, &data)) { _splitpath(data.cFileName, NULL, NULL, fname, NULL); langs->push_back(STRING(fname)); } FindClose(handle); } #else // _WIN32 DIR *dir; struct dirent *dirent; char *dot; STRING extension = STRING(".") + kTrainedDataSuffix; dir = opendir(tesseract_->datadir.string()); if (dir != NULL) { while ((dirent = readdir(dir))) { // Skip '.', '..', and hidden files if (dirent->d_name[0] != '.') { if (strstr(dirent->d_name, extension.string()) != NULL) { dot = strrchr(dirent->d_name, '.'); // This ensures that .traineddata is at the end of the file name if (strncmp(dot, extension.string(), strlen(extension.string())) == 0) { *dot = '\0'; langs->push_back(STRING(dirent->d_name)); } } } } closedir(dir); } #endif } } /** * Init only the lang model component of Tesseract. The only functions * that work after this init are SetVariable and IsValidWord. * WARNING: temporary! This function will be removed from here and placed * in a separate API at some future time. */ int TessBaseAPI::InitLangMod(const char* datapath, const char* language) { if (tesseract_ == NULL) tesseract_ = new Tesseract; else ParamUtils::ResetToDefaults(tesseract_->params()); return tesseract_->init_tesseract_lm(datapath, NULL, language); } /** * Init only for page layout analysis. Use only for calls to SetImage and * AnalysePage. Calls that attempt recognition will generate an error. */ void TessBaseAPI::InitForAnalysePage() { if (tesseract_ == NULL) { tesseract_ = new Tesseract; tesseract_->InitAdaptiveClassifier(false); } } /** * Read a "config" file containing a set of parameter name, value pairs. * Searches the standard places: tessdata/configs, tessdata/tessconfigs * and also accepts a relative or absolute path name. */ void TessBaseAPI::ReadConfigFile(const char* filename) { tesseract_->read_config_file(filename, SET_PARAM_CONSTRAINT_NON_INIT_ONLY); } /** Same as above, but only set debug params from the given config file. */ void TessBaseAPI::ReadDebugConfigFile(const char* filename) { tesseract_->read_config_file(filename, SET_PARAM_CONSTRAINT_DEBUG_ONLY); } /** * Set the current page segmentation mode. Defaults to PSM_AUTO. * The mode is stored as an IntParam so it can also be modified by * ReadConfigFile or SetVariable("tessedit_pageseg_mode", mode as string). */ void TessBaseAPI::SetPageSegMode(PageSegMode mode) { if (tesseract_ == NULL) tesseract_ = new Tesseract; tesseract_->tessedit_pageseg_mode.set_value(mode); } /** Return the current page segmentation mode. */ PageSegMode TessBaseAPI::GetPageSegMode() const { if (tesseract_ == NULL) return PSM_SINGLE_BLOCK; return static_cast( static_cast(tesseract_->tessedit_pageseg_mode)); } /** * Recognize a rectangle from an image and return the result as a string. * May be called many times for a single Init. * Currently has no error checking. * Greyscale of 8 and color of 24 or 32 bits per pixel may be given. * Palette color images will not work properly and must be converted to * 24 bit. * Binary images of 1 bit per pixel may also be given but they must be * byte packed with the MSB of the first byte being the first pixel, and a * one pixel is WHITE. For binary images set bytes_per_pixel=0. * The recognized text is returned as a char* which is coded * as UTF8 and must be freed with the delete [] operator. */ char* TessBaseAPI::TesseractRect(const unsigned char* imagedata, int bytes_per_pixel, int bytes_per_line, int left, int top, int width, int height) { if (tesseract_ == NULL || width < kMinRectSize || height < kMinRectSize) return NULL; // Nothing worth doing. // Since this original api didn't give the exact size of the image, // we have to invent a reasonable value. int bits_per_pixel = bytes_per_pixel == 0 ? 1 : bytes_per_pixel * 8; SetImage(imagedata, bytes_per_line * 8 / bits_per_pixel, height + top, bytes_per_pixel, bytes_per_line); SetRectangle(left, top, width, height); return GetUTF8Text(); } /** * Call between pages or documents etc to free up memory and forget * adaptive data. */ void TessBaseAPI::ClearAdaptiveClassifier() { if (tesseract_ == NULL) return; tesseract_->ResetAdaptiveClassifier(); tesseract_->ResetDocumentDictionary(); } /** * Provide an image for Tesseract to recognize. Format is as * TesseractRect above. Does not copy the image buffer, or take * ownership. The source image may be destroyed after Recognize is called, * either explicitly or implicitly via one of the Get*Text functions. * SetImage clears all recognition results, and sets the rectangle to the * full image, so it may be followed immediately by a GetUTF8Text, and it * will automatically perform recognition. */ void TessBaseAPI::SetImage(const unsigned char* imagedata, int width, int height, int bytes_per_pixel, int bytes_per_line) { if (InternalSetImage()) thresholder_->SetImage(imagedata, width, height, bytes_per_pixel, bytes_per_line); } void TessBaseAPI::SetSourceResolution(int ppi) { if (thresholder_) thresholder_->SetSourceYResolution(ppi); else tprintf("Please call SetImage before SetSourceResolution.\n"); } /** * Provide an image for Tesseract to recognize. As with SetImage above, * Tesseract doesn't take a copy or ownership or pixDestroy the image, so * it must persist until after Recognize. * Pix vs raw, which to use? * Use Pix where possible. A future version of Tesseract may choose to use Pix * as its internal representation and discard IMAGE altogether. * Because of that, an implementation that sources and targets Pix may end up * with less copies than an implementation that does not. */ void TessBaseAPI::SetImage(Pix* pix) { if (InternalSetImage()) thresholder_->SetImage(pix); SetInputImage(pix); } /** * Restrict recognition to a sub-rectangle of the image. Call after SetImage. * Each SetRectangle clears the recogntion results so multiple rectangles * can be recognized with the same image. */ void TessBaseAPI::SetRectangle(int left, int top, int width, int height) { if (thresholder_ == NULL) return; thresholder_->SetRectangle(left, top, width, height); ClearResults(); } /** * ONLY available after SetImage if you have Leptonica installed. * Get a copy of the internal thresholded image from Tesseract. */ Pix* TessBaseAPI::GetThresholdedImage() { if (tesseract_ == NULL || thresholder_ == NULL) return NULL; if (tesseract_->pix_binary() == NULL) Threshold(tesseract_->mutable_pix_binary()); return pixClone(tesseract_->pix_binary()); } /** * Get the result of page layout analysis as a leptonica-style * Boxa, Pixa pair, in reading order. * Can be called before or after Recognize. */ Boxa* TessBaseAPI::GetRegions(Pixa** pixa) { return GetComponentImages(RIL_BLOCK, false, pixa, NULL); } /** * Get the textlines as a leptonica-style Boxa, Pixa pair, in reading order. * Can be called before or after Recognize. * If blockids is not NULL, the block-id of each line is also returned as an * array of one element per line. delete [] after use. * If paraids is not NULL, the paragraph-id of each line within its block is * also returned as an array of one element per line. delete [] after use. */ Boxa* TessBaseAPI::GetTextlines(const bool raw_image, const int raw_padding, Pixa** pixa, int** blockids, int** paraids) { return GetComponentImages(RIL_TEXTLINE, true, raw_image, raw_padding, pixa, blockids, paraids); } /** * Get textlines and strips of image regions as a leptonica-style Boxa, Pixa * pair, in reading order. Enables downstream handling of non-rectangular * regions. * Can be called before or after Recognize. * If blockids is not NULL, the block-id of each line is also returned as an * array of one element per line. delete [] after use. */ Boxa* TessBaseAPI::GetStrips(Pixa** pixa, int** blockids) { return GetComponentImages(RIL_TEXTLINE, false, pixa, blockids); } /** * Get the words as a leptonica-style * Boxa, Pixa pair, in reading order. * Can be called before or after Recognize. */ Boxa* TessBaseAPI::GetWords(Pixa** pixa) { return GetComponentImages(RIL_WORD, true, pixa, NULL); } /** * Gets the individual connected (text) components (created * after pages segmentation step, but before recognition) * as a leptonica-style Boxa, Pixa pair, in reading order. * Can be called before or after Recognize. */ Boxa* TessBaseAPI::GetConnectedComponents(Pixa** pixa) { return GetComponentImages(RIL_SYMBOL, true, pixa, NULL); } /** * Get the given level kind of components (block, textline, word etc.) as a * leptonica-style Boxa, Pixa pair, in reading order. * Can be called before or after Recognize. * If blockids is not NULL, the block-id of each component is also returned * as an array of one element per component. delete [] after use. * If text_only is true, then only text components are returned. */ Boxa* TessBaseAPI::GetComponentImages(PageIteratorLevel level, bool text_only, bool raw_image, const int raw_padding, Pixa** pixa, int** blockids, int** paraids) { PageIterator* page_it = GetIterator(); if (page_it == NULL) page_it = AnalyseLayout(); if (page_it == NULL) return NULL; // Failed. // Count the components to get a size for the arrays. int component_count = 0; int left, top, right, bottom; TessResultCallback* get_bbox = NULL; if (raw_image) { // Get bounding box in original raw image with padding. get_bbox = NewPermanentTessCallback(page_it, &PageIterator::BoundingBox, level, raw_padding, &left, &top, &right, &bottom); } else { // Get bounding box from binarized imaged. Note that this could be // differently scaled from the original image. get_bbox = NewPermanentTessCallback(page_it, &PageIterator::BoundingBoxInternal, level, &left, &top, &right, &bottom); } do { if (get_bbox->Run() && (!text_only || PTIsTextType(page_it->BlockType()))) ++component_count; } while (page_it->Next(level)); Boxa* boxa = boxaCreate(component_count); if (pixa != NULL) *pixa = pixaCreate(component_count); if (blockids != NULL) *blockids = new int[component_count]; if (paraids != NULL) *paraids = new int[component_count]; int blockid = 0; int paraid = 0; int component_index = 0; page_it->Begin(); do { if (get_bbox->Run() && (!text_only || PTIsTextType(page_it->BlockType()))) { Box* lbox = boxCreate(left, top, right - left, bottom - top); boxaAddBox(boxa, lbox, L_INSERT); if (pixa != NULL) { Pix* pix = NULL; if (raw_image) { pix = page_it->GetImage(level, raw_padding, input_image_, &left, &top); } else { pix = page_it->GetBinaryImage(level); } pixaAddPix(*pixa, pix, L_INSERT); pixaAddBox(*pixa, lbox, L_CLONE); } if (paraids != NULL) { (*paraids)[component_index] = paraid; if (page_it->IsAtFinalElement(RIL_PARA, level)) ++paraid; } if (blockids != NULL) { (*blockids)[component_index] = blockid; if (page_it->IsAtFinalElement(RIL_BLOCK, level)) { ++blockid; paraid = 0; } } ++component_index; } } while (page_it->Next(level)); delete page_it; delete get_bbox; return boxa; } int TessBaseAPI::GetThresholdedImageScaleFactor() const { if (thresholder_ == NULL) { return 0; } return thresholder_->GetScaleFactor(); } /** Dump the internal binary image to a PGM file. */ void TessBaseAPI::DumpPGM(const char* filename) { if (tesseract_ == NULL) return; FILE *fp = fopen(filename, "wb"); Pix* pix = tesseract_->pix_binary(); int width = pixGetWidth(pix); int height = pixGetHeight(pix); l_uint32* data = pixGetData(pix); fprintf(fp, "P5 %d %d 255\n", width, height); for (int y = 0; y < height; ++y, data += pixGetWpl(pix)) { for (int x = 0; x < width; ++x) { uinT8 b = GET_DATA_BIT(data, x) ? 0 : 255; fwrite(&b, 1, 1, fp); } } fclose(fp); } #ifndef NO_CUBE_BUILD /** * Placeholder for call to Cube and test that the input data is correct. * reskew is the direction of baselines in the skewed image in * normalized (cos theta, sin theta) form, so (0.866, 0.5) would represent * a 30 degree anticlockwise skew. */ int CubeAPITest(Boxa* boxa_blocks, Pixa* pixa_blocks, Boxa* boxa_words, Pixa* pixa_words, const FCOORD& reskew, Pix* page_pix, PAGE_RES* page_res) { int block_count = boxaGetCount(boxa_blocks); ASSERT_HOST(block_count == pixaGetCount(pixa_blocks)); // Write each block to the current directory as junk_write_display.nnn.png. for (int i = 0; i < block_count; ++i) { Pix* pix = pixaGetPix(pixa_blocks, i, L_CLONE); pixDisplayWrite(pix, 1); } int word_count = boxaGetCount(boxa_words); ASSERT_HOST(word_count == pixaGetCount(pixa_words)); int pr_word = 0; PAGE_RES_IT page_res_it(page_res); for (page_res_it.restart_page(); page_res_it.word () != NULL; page_res_it.forward(), ++pr_word) { WERD_RES *word = page_res_it.word(); WERD_CHOICE* choice = word->best_choice; // Write the first 100 words to files names wordims/.tif. if (pr_word < 100) { STRING filename("wordims/"); if (choice != NULL) { filename += choice->unichar_string(); } else { char numbuf[32]; filename += "unclassified"; snprintf(numbuf, 32, "%03d", pr_word); filename += numbuf; } filename += ".tif"; Pix* pix = pixaGetPix(pixa_words, pr_word, L_CLONE); pixWrite(filename.string(), pix, IFF_TIFF_G4); } } ASSERT_HOST(pr_word == word_count); return 0; } #endif // NO_CUBE_BUILD /** * Runs page layout analysis in the mode set by SetPageSegMode. * May optionally be called prior to Recognize to get access to just * the page layout results. Returns an iterator to the results. * If merge_similar_words is true, words are combined where suitable for use * with a line recognizer. Use if you want to use AnalyseLayout to find the * textlines, and then want to process textline fragments with an external * line recognizer. * Returns NULL on error or an empty page. * The returned iterator must be deleted after use. * WARNING! This class points to data held within the TessBaseAPI class, and * therefore can only be used while the TessBaseAPI class still exists and * has not been subjected to a call of Init, SetImage, Recognize, Clear, End * DetectOS, or anything else that changes the internal PAGE_RES. */ PageIterator* TessBaseAPI::AnalyseLayout(bool merge_similar_words) { if (FindLines() == 0) { if (block_list_->empty()) return NULL; // The page was empty. page_res_ = new PAGE_RES(merge_similar_words, block_list_, NULL); DetectParagraphs(false); return new PageIterator( page_res_, tesseract_, thresholder_->GetScaleFactor(), thresholder_->GetScaledYResolution(), rect_left_, rect_top_, rect_width_, rect_height_); } return NULL; } /** * Recognize the tesseract global image and return the result as Tesseract * internal structures. */ int TessBaseAPI::Recognize(ETEXT_DESC* monitor) { if (tesseract_ == NULL) return -1; if (FindLines() != 0) return -1; if (page_res_ != NULL) delete page_res_; if (block_list_->empty()) { page_res_ = new PAGE_RES(false, block_list_, &tesseract_->prev_word_best_choice_); return 0; // Empty page. } tesseract_->SetBlackAndWhitelist(); recognition_done_ = true; if (tesseract_->tessedit_resegment_from_line_boxes) { page_res_ = tesseract_->ApplyBoxes(*input_file_, true, block_list_); } else if (tesseract_->tessedit_resegment_from_boxes) { page_res_ = tesseract_->ApplyBoxes(*input_file_, false, block_list_); } else { // TODO(rays) LSTM here. page_res_ = new PAGE_RES(false, block_list_, &tesseract_->prev_word_best_choice_); } if (tesseract_->tessedit_make_boxes_from_boxes) { tesseract_->CorrectClassifyWords(page_res_); return 0; } if (truth_cb_ != NULL) { tesseract_->wordrec_run_blamer.set_value(true); PageIterator *page_it = new PageIterator( page_res_, tesseract_, thresholder_->GetScaleFactor(), thresholder_->GetScaledYResolution(), rect_left_, rect_top_, rect_width_, rect_height_); truth_cb_->Run(tesseract_->getDict().getUnicharset(), image_height_, page_it, this->tesseract()->pix_grey()); delete page_it; } int result = 0; if (tesseract_->interactive_display_mode) { #ifndef GRAPHICS_DISABLED tesseract_->pgeditor_main(rect_width_, rect_height_, page_res_); #endif // GRAPHICS_DISABLED // The page_res is invalid after an interactive session, so cleanup // in a way that lets us continue to the next page without crashing. delete page_res_; page_res_ = NULL; return -1; } else if (tesseract_->tessedit_train_from_boxes) { STRING fontname; ExtractFontName(*output_file_, &fontname); tesseract_->ApplyBoxTraining(fontname, page_res_); } else if (tesseract_->tessedit_ambigs_training) { FILE *training_output_file = tesseract_->init_recog_training(*input_file_); // OCR the page segmented into words by tesseract. tesseract_->recog_training_segmented( *input_file_, page_res_, monitor, training_output_file); fclose(training_output_file); } else { // Now run the main recognition. bool wait_for_text = true; GetBoolVariable("paragraph_text_based", &wait_for_text); if (!wait_for_text) DetectParagraphs(false); if (tesseract_->recog_all_words(page_res_, monitor, NULL, NULL, 0)) { if (wait_for_text) DetectParagraphs(true); } else { result = -1; } } return result; } /** Tests the chopper by exhaustively running chop_one_blob. */ int TessBaseAPI::RecognizeForChopTest(ETEXT_DESC* monitor) { if (tesseract_ == NULL) return -1; if (thresholder_ == NULL || thresholder_->IsEmpty()) { tprintf("Please call SetImage before attempting recognition."); return -1; } if (page_res_ != NULL) ClearResults(); if (FindLines() != 0) return -1; // Additional conditions under which chopper test cannot be run if (tesseract_->interactive_display_mode) return -1; recognition_done_ = true; page_res_ = new PAGE_RES(false, block_list_, &(tesseract_->prev_word_best_choice_)); PAGE_RES_IT page_res_it(page_res_); while (page_res_it.word() != NULL) { WERD_RES *word_res = page_res_it.word(); GenericVector boxes; tesseract_->MaximallyChopWord(boxes, page_res_it.block()->block, page_res_it.row()->row, word_res); page_res_it.forward(); } return 0; } void TessBaseAPI::SetInputImage(Pix *pix) { if (input_image_) pixDestroy(&input_image_); input_image_ = NULL; if (pix) input_image_ = pixCopy(NULL, pix); } Pix* TessBaseAPI::GetInputImage() { return input_image_; } const char * TessBaseAPI::GetInputName() { if (input_file_) return input_file_->c_str(); return NULL; } const char * TessBaseAPI::GetDatapath() { return tesseract_->datadir.c_str(); } int TessBaseAPI::GetSourceYResolution() { return thresholder_->GetSourceYResolution(); } // If flist exists, get data from there. Otherwise get data from buf. // Seems convoluted, but is the easiest way I know of to meet multiple // goals. Support streaming from stdin, and also work on platforms // lacking fmemopen. bool TessBaseAPI::ProcessPagesFileList(FILE *flist, STRING *buf, const char* retry_config, int timeout_millisec, TessResultRenderer* renderer, int tessedit_page_number) { if (!flist && !buf) return false; int page = (tessedit_page_number >= 0) ? tessedit_page_number : 0; char pagename[MAX_PATH]; GenericVector lines; if (!flist) { buf->split('\n', &lines); if (lines.empty()) return false; } // Skip to the requested page number. for (int i = 0; i < page; i++) { if (flist) { if (fgets(pagename, sizeof(pagename), flist) == NULL) break; } } // Begin producing output const char* kUnknownTitle = ""; if (renderer && !renderer->BeginDocument(kUnknownTitle)) { return false; } // Loop over all pages - or just the requested one while (true) { if (flist) { if (fgets(pagename, sizeof(pagename), flist) == NULL) break; } else { if (page >= lines.size()) break; snprintf(pagename, sizeof(pagename), "%s", lines[page].c_str()); } chomp_string(pagename); Pix *pix = pixRead(pagename); if (pix == NULL) { tprintf("Image file %s cannot be read!\n", pagename); return false; } tprintf("Page %d : %s\n", page, pagename); bool r = ProcessPage(pix, page, pagename, retry_config, timeout_millisec, renderer); pixDestroy(&pix); if (!r) return false; if (tessedit_page_number >= 0) break; ++page; } // Finish producing output if (renderer && !renderer->EndDocument()) { return false; } return true; } bool TessBaseAPI::ProcessPagesMultipageTiff(const l_uint8 *data, size_t size, const char* filename, const char* retry_config, int timeout_millisec, TessResultRenderer* renderer, int tessedit_page_number) { #ifndef ANDROID_BUILD Pix *pix = NULL; #ifdef USE_OPENCL OpenclDevice od; #endif // USE_OPENCL int page = (tessedit_page_number >= 0) ? tessedit_page_number : 0; for (; ; ++page) { if (tessedit_page_number >= 0) page = tessedit_page_number; #ifdef USE_OPENCL if ( od.selectedDeviceIsOpenCL() ) { // FIXME(jbreiden) Not implemented. pix = od.pixReadMemTiffCl(data, size, page); } else { #endif // USE_OPENCL pix = pixReadMemTiff(data, size, page); #ifdef USE_OPENCL } #endif // USE_OPENCL if (pix == NULL) break; tprintf("Page %d\n", page + 1); char page_str[kMaxIntSize]; snprintf(page_str, kMaxIntSize - 1, "%d", page); SetVariable("applybox_page", page_str); bool r = ProcessPage(pix, page, filename, retry_config, timeout_millisec, renderer); pixDestroy(&pix); if (!r) return false; if (tessedit_page_number >= 0) break; } return true; #else return false; #endif } // Master ProcessPages calls ProcessPagesInternal and then does any post- // processing required due to being in a training mode. bool TessBaseAPI::ProcessPages(const char* filename, const char* retry_config, int timeout_millisec, TessResultRenderer* renderer) { bool result = ProcessPagesInternal(filename, retry_config, timeout_millisec, renderer); if (result) { if (tesseract_->tessedit_train_from_boxes && !tesseract_->WriteTRFile(*output_file_)) { tprintf("Write of TR file failed: %s\n", output_file_->string()); return false; } } return result; } // In the ideal scenario, Tesseract will start working on data as soon // as it can. For example, if you steam a filelist through stdin, we // should start the OCR process as soon as the first filename is // available. This is particularly useful when hooking Tesseract up to // slow hardware such as a book scanning machine. // // Unfortunately there are tradeoffs. You can't seek on stdin. That // makes automatic detection of datatype (TIFF? filelist? PNG?) // impractical. So we support a command line flag to explicitly // identify the scenario that really matters: filelists on // stdin. We'll still do our best if the user likes pipes. That means // piling up any data coming into stdin into a memory buffer. bool TessBaseAPI::ProcessPagesInternal(const char* filename, const char* retry_config, int timeout_millisec, TessResultRenderer* renderer) { #ifndef ANDROID_BUILD PERF_COUNT_START("ProcessPages") bool stdInput = !strcmp(filename, "stdin") || !strcmp(filename, "-"); if (stdInput) { #ifdef WIN32 if (_setmode(_fileno(stdin), _O_BINARY) == -1) tprintf("ERROR: cin to binary: %s", strerror(errno)); #endif // WIN32 } if (stream_filelist) { return ProcessPagesFileList(stdin, NULL, retry_config, timeout_millisec, renderer, tesseract_->tessedit_page_number); } // At this point we are officially in autodection territory. // That means we are going to buffer stdin so that it is // seekable. To keep code simple we will also buffer data // coming from a file. std::string buf; if (stdInput) { buf.assign((std::istreambuf_iterator(std::cin)), (std::istreambuf_iterator())); } else { std::ifstream ifs(filename, std::ios::binary); if (ifs) { buf.assign((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); } else { tprintf("ERROR: Can not open input file %s\n", filename); return false; } } // Here is our autodetection int format; const l_uint8 * data = reinterpret_cast(buf.c_str()); findFileFormatBuffer(data, &format); // Maybe we have a filelist if (format == IFF_UNKNOWN) { STRING s(buf.c_str()); return ProcessPagesFileList(NULL, &s, retry_config, timeout_millisec, renderer, tesseract_->tessedit_page_number); } // Maybe we have a TIFF which is potentially multipage bool tiff = (format == IFF_TIFF || format == IFF_TIFF_PACKBITS || format == IFF_TIFF_RLE || format == IFF_TIFF_G3 || format == IFF_TIFF_G4 || format == IFF_TIFF_LZW || format == IFF_TIFF_ZIP); // Fail early if we can, before producing any output Pix *pix = NULL; if (!tiff) { pix = pixReadMem(data, buf.size()); if (pix == NULL) { return false; } } // Begin the output const char* kUnknownTitle = ""; if (renderer && !renderer->BeginDocument(kUnknownTitle)) { pixDestroy(&pix); return false; } // Produce output bool r = false; if (tiff) { r = ProcessPagesMultipageTiff(data, buf.size(), filename, retry_config, timeout_millisec, renderer, tesseract_->tessedit_page_number); } else { r = ProcessPage(pix, 0, filename, retry_config, timeout_millisec, renderer); pixDestroy(&pix); } // End the output if (!r || (renderer && !renderer->EndDocument())) { return false; } PERF_COUNT_END return true; #else return false; #endif } bool TessBaseAPI::ProcessPage(Pix* pix, int page_index, const char* filename, const char* retry_config, int timeout_millisec, TessResultRenderer* renderer) { PERF_COUNT_START("ProcessPage") SetInputName(filename); SetImage(pix); bool failed = false; if (tesseract_->tessedit_pageseg_mode == PSM_AUTO_ONLY) { // Disabled character recognition PageIterator* it = AnalyseLayout(); if (it == NULL) { failed = true; } else { delete it; } } else if (tesseract_->tessedit_pageseg_mode == PSM_OSD_ONLY) { failed = FindLines() != 0; } else if (timeout_millisec > 0) { // Running with a timeout. ETEXT_DESC monitor; monitor.cancel = NULL; monitor.cancel_this = NULL; monitor.set_deadline_msecs(timeout_millisec); // Now run the main recognition. failed = Recognize(&monitor) < 0; } else { // Normal layout and character recognition with no timeout. failed = Recognize(NULL) < 0; } if (tesseract_->tessedit_write_images) { #ifndef ANDROID_BUILD Pix* page_pix = GetThresholdedImage(); pixWrite("tessinput.tif", page_pix, IFF_TIFF_G4); #endif // ANDROID_BUILD } if (failed && retry_config != NULL && retry_config[0] != '\0') { // Save current config variables before switching modes. FILE* fp = fopen(kOldVarsFile, "wb"); PrintVariables(fp); fclose(fp); // Switch to alternate mode for retry. ReadConfigFile(retry_config); SetImage(pix); Recognize(NULL); // Restore saved config variables. ReadConfigFile(kOldVarsFile); } if (renderer && !failed) { failed = !renderer->AddImage(this); } PERF_COUNT_END return !failed; } /** * Get a left-to-right iterator to the results of LayoutAnalysis and/or * Recognize. The returned iterator must be deleted after use. */ LTRResultIterator* TessBaseAPI::GetLTRIterator() { if (tesseract_ == NULL || page_res_ == NULL) return NULL; return new LTRResultIterator( page_res_, tesseract_, thresholder_->GetScaleFactor(), thresholder_->GetScaledYResolution(), rect_left_, rect_top_, rect_width_, rect_height_); } /** * Get a reading-order iterator to the results of LayoutAnalysis and/or * Recognize. The returned iterator must be deleted after use. * WARNING! This class points to data held within the TessBaseAPI class, and * therefore can only be used while the TessBaseAPI class still exists and * has not been subjected to a call of Init, SetImage, Recognize, Clear, End * DetectOS, or anything else that changes the internal PAGE_RES. */ ResultIterator* TessBaseAPI::GetIterator() { if (tesseract_ == NULL || page_res_ == NULL) return NULL; return ResultIterator::StartOfParagraph(LTRResultIterator( page_res_, tesseract_, thresholder_->GetScaleFactor(), thresholder_->GetScaledYResolution(), rect_left_, rect_top_, rect_width_, rect_height_)); } /** * Get a mutable iterator to the results of LayoutAnalysis and/or Recognize. * The returned iterator must be deleted after use. * WARNING! This class points to data held within the TessBaseAPI class, and * therefore can only be used while the TessBaseAPI class still exists and * has not been subjected to a call of Init, SetImage, Recognize, Clear, End * DetectOS, or anything else that changes the internal PAGE_RES. */ MutableIterator* TessBaseAPI::GetMutableIterator() { if (tesseract_ == NULL || page_res_ == NULL) return NULL; return new MutableIterator(page_res_, tesseract_, thresholder_->GetScaleFactor(), thresholder_->GetScaledYResolution(), rect_left_, rect_top_, rect_width_, rect_height_); } /** Make a text string from the internal data structures. */ char* TessBaseAPI::GetUTF8Text() { if (tesseract_ == NULL || (!recognition_done_ && Recognize(NULL) < 0)) return NULL; STRING text(""); ResultIterator *it = GetIterator(); do { if (it->Empty(RIL_PARA)) continue; char *para_text = it->GetUTF8Text(RIL_PARA); text += para_text; delete []para_text; } while (it->Next(RIL_PARA)); char* result = new char[text.length() + 1]; strncpy(result, text.string(), text.length() + 1); delete it; return result; } /** * Gets the block orientation at the current iterator position. */ static tesseract::Orientation GetBlockTextOrientation(const PageIterator *it) { tesseract::Orientation orientation; tesseract::WritingDirection writing_direction; tesseract::TextlineOrder textline_order; float deskew_angle; it->Orientation(&orientation, &writing_direction, &textline_order, &deskew_angle); return orientation; } /** * Fits a line to the baseline at the given level, and appends its coefficients * to the hOCR string. * NOTE: The hOCR spec is unclear on how to specify baseline coefficients for * rotated textlines. For this reason, on textlines that are not upright, this * method currently only inserts a 'textangle' property to indicate the rotation * direction and does not add any baseline information to the hocr string. */ static void AddBaselineCoordsTohOCR(const PageIterator *it, PageIteratorLevel level, STRING* hocr_str) { tesseract::Orientation orientation = GetBlockTextOrientation(it); if (orientation != ORIENTATION_PAGE_UP) { hocr_str->add_str_int("; textangle ", 360 - orientation * 90); return; } int left, top, right, bottom; it->BoundingBox(level, &left, &top, &right, &bottom); // Try to get the baseline coordinates at this level. int x1, y1, x2, y2; if (!it->Baseline(level, &x1, &y1, &x2, &y2)) return; // Following the description of this field of the hOCR spec, we convert the // baseline coordinates so that "the bottom left of the bounding box is the // origin". x1 -= left; x2 -= left; y1 -= bottom; y2 -= bottom; // Now fit a line through the points so we can extract coefficients for the // equation: y = p1 x + p0 double p1 = 0; double p0 = 0; if (x1 == x2) { // Problem computing the polynomial coefficients. return; } p1 = (y2 - y1) / static_cast(x2 - x1); p0 = y1 - static_cast(p1 * x1); hocr_str->add_str_double("; baseline ", round(p1 * 1000.0) / 1000.0); hocr_str->add_str_double(" ", round(p0 * 1000.0) / 1000.0); } static void AddIdTohOCR(STRING* hocr_str, const std::string base, int num1, int num2) { unsigned long bufsize = base.length() + 2 * kMaxIntSize; char id_buffer[bufsize]; if (num2 >= 0) { snprintf(id_buffer, bufsize - 1, "%s_%d_%d", base.c_str(), num1, num2); } else { snprintf(id_buffer, bufsize - 1, "%s_%d", base.c_str(), num1); } id_buffer[bufsize - 1] = '\0'; *hocr_str += " id='"; *hocr_str += id_buffer; *hocr_str += "'"; } static void AddBoxTohOCR(const ResultIterator *it, PageIteratorLevel level, STRING* hocr_str) { int left, top, right, bottom; it->BoundingBox(level, &left, &top, &right, &bottom); // This is the only place we use double quotes instead of single quotes, // but it may too late to change for consistency hocr_str->add_str_int(" title=\"bbox ", left); hocr_str->add_str_int(" ", top); hocr_str->add_str_int(" ", right); hocr_str->add_str_int(" ", bottom); // Add baseline coordinates & heights for textlines only. if (level == RIL_TEXTLINE) { AddBaselineCoordsTohOCR(it, level, hocr_str); // add custom height measures float row_height, descenders, ascenders; // row attributes it->RowAttributes(&row_height, &descenders, &ascenders); // TODO: Do we want to limit these to a single decimal place? hocr_str->add_str_double("; x_size ", row_height); hocr_str->add_str_double("; x_descenders ", descenders * -1); hocr_str->add_str_double("; x_ascenders ", ascenders); } *hocr_str += "\">"; } /** * Make a HTML-formatted string with hOCR markup from the internal * data structures. * page_number is 0-based but will appear in the output as 1-based. * Image name/input_file_ can be set by SetInputName before calling * GetHOCRText * STL removed from original patch submission and refactored by rays. */ char* TessBaseAPI::GetHOCRText(int page_number) { if (tesseract_ == NULL || (page_res_ == NULL && Recognize(NULL) < 0)) return NULL; int lcnt = 1, bcnt = 1, pcnt = 1, wcnt = 1; int page_id = page_number + 1; // hOCR uses 1-based page numbers. bool font_info = false; GetBoolVariable("hocr_font_info", &font_info); STRING hocr_str(""); if (input_file_ == NULL) SetInputName(NULL); #ifdef _WIN32 // convert input name from ANSI encoding to utf-8 int str16_len = MultiByteToWideChar(CP_ACP, 0, input_file_->string(), -1, NULL, 0); wchar_t *uni16_str = new WCHAR[str16_len]; str16_len = MultiByteToWideChar(CP_ACP, 0, input_file_->string(), -1, uni16_str, str16_len); int utf8_len = WideCharToMultiByte(CP_UTF8, 0, uni16_str, str16_len, NULL, 0, NULL, NULL); char *utf8_str = new char[utf8_len]; WideCharToMultiByte(CP_UTF8, 0, uni16_str, str16_len, utf8_str, utf8_len, NULL, NULL); *input_file_ = utf8_str; delete[] uni16_str; delete[] utf8_str; #endif hocr_str += "
string()); } else { hocr_str += "unknown"; } hocr_str.add_str_int("\"; bbox ", rect_left_); hocr_str.add_str_int(" ", rect_top_); hocr_str.add_str_int(" ", rect_width_); hocr_str.add_str_int(" ", rect_height_); hocr_str.add_str_int("; ppageno ", page_number); hocr_str += "'>\n"; ResultIterator *res_it = GetIterator(); while (!res_it->Empty(RIL_BLOCK)) { if (res_it->Empty(RIL_WORD)) { res_it->Next(RIL_WORD); continue; } // Open any new block/paragraph/textline. if (res_it->IsAtBeginningOf(RIL_BLOCK)) { hocr_str += "
IsAtBeginningOf(RIL_PARA)) { hocr_str += "\n

ParagraphIsLtr()) { hocr_str += " dir='ltr'"; } else { hocr_str += " dir='rtl'"; } AddIdTohOCR(&hocr_str, "par", page_id, pcnt); AddBoxTohOCR(res_it, RIL_PARA, &hocr_str); } if (res_it->IsAtBeginningOf(RIL_TEXTLINE)) { hocr_str += "\n BoundingBox(RIL_WORD, &left, &top, &right, &bottom); font_name = res_it->WordFontAttributes(&bold, &italic, &underlined, &monospace, &serif, &smallcaps, &pointsize, &font_id); hocr_str.add_str_int(" title='bbox ", left); hocr_str.add_str_int(" ", top); hocr_str.add_str_int(" ", right); hocr_str.add_str_int(" ", bottom); hocr_str.add_str_int("; x_wconf ", res_it->Confidence(RIL_WORD)); if (font_info) { if (font_name) { hocr_str += "; x_font "; hocr_str += HOcrEscape(font_name); } hocr_str.add_str_int("; x_fsize ", pointsize); } hocr_str += "'"; if (res_it->WordRecognitionLanguage()) { hocr_str += " lang='"; hocr_str += res_it->WordRecognitionLanguage(); hocr_str += "'"; } switch (res_it->WordDirection()) { case DIR_LEFT_TO_RIGHT: hocr_str += " dir='ltr'"; break; case DIR_RIGHT_TO_LEFT: hocr_str += " dir='rtl'"; break; default: // Do nothing. break; } hocr_str += ">"; bool last_word_in_line = res_it->IsAtFinalElement(RIL_TEXTLINE, RIL_WORD); bool last_word_in_para = res_it->IsAtFinalElement(RIL_PARA, RIL_WORD); bool last_word_in_block = res_it->IsAtFinalElement(RIL_BLOCK, RIL_WORD); if (bold) hocr_str += ""; if (italic) hocr_str += ""; do { const char *grapheme = res_it->GetUTF8Text(RIL_SYMBOL); if (grapheme && grapheme[0] != 0) { hocr_str += HOcrEscape(grapheme); } delete []grapheme; res_it->Next(RIL_SYMBOL); } while (!res_it->Empty(RIL_BLOCK) && !res_it->IsAtBeginningOf(RIL_WORD)); if (italic) hocr_str += ""; if (bold) hocr_str += ""; hocr_str += " "; wcnt++; // Close any ending block/paragraph/textline. if (last_word_in_line) { hocr_str += "\n "; lcnt++; } if (last_word_in_para) { hocr_str += "\n

\n"; pcnt++; } if (last_word_in_block) { hocr_str += "
\n"; bcnt++; } } hocr_str += "
\n"; char *ret = new char[hocr_str.length() + 1]; strcpy(ret, hocr_str.string()); delete res_it; return ret; } /** The 5 numbers output for each box (the usual 4 and a page number.) */ const int kNumbersPerBlob = 5; /** * The number of bytes taken by each number. Since we use inT16 for ICOORD, * assume only 5 digits max. */ const int kBytesPerNumber = 5; /** * Multiplier for max expected textlength assumes (kBytesPerNumber + space) * * kNumbersPerBlob plus the newline. Add to this the * original UTF8 characters, and one kMaxBytesPerLine for safety. */ const int kBytesPerBlob = kNumbersPerBlob * (kBytesPerNumber + 1) + 1; const int kBytesPerBoxFileLine = (kBytesPerNumber + 1) * kNumbersPerBlob + 1; /** Max bytes in the decimal representation of inT64. */ const int kBytesPer64BitNumber = 20; /** * A maximal single box could occupy kNumbersPerBlob numbers at * kBytesPer64BitNumber digits (if someone sneaks in a 64 bit value) and a * space plus the newline and the maximum length of a UNICHAR. * Test against this on each iteration for safety. */ const int kMaxBytesPerLine = kNumbersPerBlob * (kBytesPer64BitNumber + 1) + 1 + UNICHAR_LEN; /** * The recognized text is returned as a char* which is coded * as a UTF8 box file and must be freed with the delete [] operator. * page_number is a 0-base page index that will appear in the box file. */ char* TessBaseAPI::GetBoxText(int page_number) { if (tesseract_ == NULL || (!recognition_done_ && Recognize(NULL) < 0)) return NULL; int blob_count; int utf8_length = TextLength(&blob_count); int total_length = blob_count * kBytesPerBoxFileLine + utf8_length + kMaxBytesPerLine; char* result = new char[total_length]; strcpy(result, "\0"); int output_length = 0; LTRResultIterator* it = GetLTRIterator(); do { int left, top, right, bottom; if (it->BoundingBox(RIL_SYMBOL, &left, &top, &right, &bottom)) { char* text = it->GetUTF8Text(RIL_SYMBOL); // Tesseract uses space for recognition failure. Fix to a reject // character, kTesseractReject so we don't create illegal box files. for (int i = 0; text[i] != '\0'; ++i) { if (text[i] == ' ') text[i] = kTesseractReject; } snprintf(result + output_length, total_length - output_length, "%s %d %d %d %d %d\n", text, left, image_height_ - bottom, right, image_height_ - top, page_number); output_length += strlen(result + output_length); delete [] text; // Just in case... if (output_length + kMaxBytesPerLine > total_length) break; } } while (it->Next(RIL_SYMBOL)); delete it; return result; } /** * Conversion table for non-latin characters. * Maps characters out of the latin set into the latin set. * TODO(rays) incorporate this translation into unicharset. */ const int kUniChs[] = { 0x20ac, 0x201c, 0x201d, 0x2018, 0x2019, 0x2022, 0x2014, 0 }; /** Latin chars corresponding to the unicode chars above. */ const int kLatinChs[] = { 0x00a2, 0x0022, 0x0022, 0x0027, 0x0027, 0x00b7, 0x002d, 0 }; /** * The recognized text is returned as a char* which is coded * as UNLV format Latin-1 with specific reject and suspect codes * and must be freed with the delete [] operator. */ char* TessBaseAPI::GetUNLVText() { if (tesseract_ == NULL || (!recognition_done_ && Recognize(NULL) < 0)) return NULL; bool tilde_crunch_written = false; bool last_char_was_newline = true; bool last_char_was_tilde = false; int total_length = TextLength(NULL); PAGE_RES_IT page_res_it(page_res_); char* result = new char[total_length]; char* ptr = result; for (page_res_it.restart_page(); page_res_it.word () != NULL; page_res_it.forward()) { WERD_RES *word = page_res_it.word(); // Process the current word. if (word->unlv_crunch_mode != CR_NONE) { if (word->unlv_crunch_mode != CR_DELETE && (!tilde_crunch_written || (word->unlv_crunch_mode == CR_KEEP_SPACE && word->word->space() > 0 && !word->word->flag(W_FUZZY_NON) && !word->word->flag(W_FUZZY_SP)))) { if (!word->word->flag(W_BOL) && word->word->space() > 0 && !word->word->flag(W_FUZZY_NON) && !word->word->flag(W_FUZZY_SP)) { /* Write a space to separate from preceding good text */ *ptr++ = ' '; last_char_was_tilde = false; } if (!last_char_was_tilde) { // Write a reject char. last_char_was_tilde = true; *ptr++ = kUNLVReject; tilde_crunch_written = true; last_char_was_newline = false; } } } else { // NORMAL PROCESSING of non tilde crunched words. tilde_crunch_written = false; tesseract_->set_unlv_suspects(word); const char* wordstr = word->best_choice->unichar_string().string(); const STRING& lengths = word->best_choice->unichar_lengths(); int length = lengths.length(); int i = 0; int offset = 0; if (last_char_was_tilde && word->word->space() == 0 && wordstr[offset] == ' ') { // Prevent adjacent tilde across words - we know that adjacent tildes // within words have been removed. // Skip the first character. offset = lengths[i++]; } if (i < length && wordstr[offset] != 0) { if (!last_char_was_newline) *ptr++ = ' '; else last_char_was_newline = false; for (; i < length; offset += lengths[i++]) { if (wordstr[offset] == ' ' || wordstr[offset] == kTesseractReject) { *ptr++ = kUNLVReject; last_char_was_tilde = true; } else { if (word->reject_map[i].rejected()) *ptr++ = kUNLVSuspect; UNICHAR ch(wordstr + offset, lengths[i]); int uni_ch = ch.first_uni(); for (int j = 0; kUniChs[j] != 0; ++j) { if (kUniChs[j] == uni_ch) { uni_ch = kLatinChs[j]; break; } } if (uni_ch <= 0xff) { *ptr++ = static_cast(uni_ch); last_char_was_tilde = false; } else { *ptr++ = kUNLVReject; last_char_was_tilde = true; } } } } } if (word->word->flag(W_EOL) && !last_char_was_newline) { /* Add a new line output */ *ptr++ = '\n'; tilde_crunch_written = false; last_char_was_newline = true; last_char_was_tilde = false; } } *ptr++ = '\n'; *ptr = '\0'; return result; } /** * The recognized text is returned as a char* which is coded * as UTF8 and must be freed with the delete [] operator. * page_number is a 0-based page index that will appear in the osd file. */ char* TessBaseAPI::GetOsdText(int page_number) { OSResults osr; bool osd = DetectOS(&osr); if (!osd) { return NULL; } int orient_id = osr.best_result.orientation_id; int script_id = osr.get_best_script(orient_id); float orient_conf = osr.best_result.oconfidence; float script_conf = osr.best_result.sconfidence; const char* script_name = osr.unicharset->get_script_from_script_id(script_id); // clockwise orientation of the input image, in degrees int orient_deg = orient_id * 90; // clockwise rotation needed to make the page upright int rotate = OrientationIdToValue(orient_id); char* osd_buf = new char[255]; snprintf(osd_buf, 255, "Page number: %d\n" "Orientation in degrees: %d\n" "Rotate: %d\n" "Orientation confidence: %.2f\n" "Script: %s\n" "Script confidence: %.2f\n", page_number, orient_deg, rotate, orient_conf, script_name, script_conf); return osd_buf; } /** Returns the average word confidence for Tesseract page result. */ int TessBaseAPI::MeanTextConf() { int* conf = AllWordConfidences(); if (!conf) return 0; int sum = 0; int *pt = conf; while (*pt >= 0) sum += *pt++; if (pt != conf) sum /= pt - conf; delete [] conf; return sum; } /** Returns an array of all word confidences, terminated by -1. */ int* TessBaseAPI::AllWordConfidences() { if (tesseract_ == NULL || (!recognition_done_ && Recognize(NULL) < 0)) return NULL; int n_word = 0; PAGE_RES_IT res_it(page_res_); for (res_it.restart_page(); res_it.word() != NULL; res_it.forward()) n_word++; int* conf = new int[n_word+1]; n_word = 0; for (res_it.restart_page(); res_it.word() != NULL; res_it.forward()) { WERD_RES *word = res_it.word(); WERD_CHOICE* choice = word->best_choice; int w_conf = static_cast(100 + 5 * choice->certainty()); // This is the eq for converting Tesseract confidence to 1..100 if (w_conf < 0) w_conf = 0; if (w_conf > 100) w_conf = 100; conf[n_word++] = w_conf; } conf[n_word] = -1; return conf; } /** * Applies the given word to the adaptive classifier if possible. * The word must be SPACE-DELIMITED UTF-8 - l i k e t h i s , so it can * tell the boundaries of the graphemes. * Assumes that SetImage/SetRectangle have been used to set the image * to the given word. The mode arg should be PSM_SINGLE_WORD or * PSM_CIRCLE_WORD, as that will be used to control layout analysis. * The currently set PageSegMode is preserved. * Returns false if adaption was not possible for some reason. */ bool TessBaseAPI::AdaptToWordStr(PageSegMode mode, const char* wordstr) { int debug = 0; GetIntVariable("applybox_debug", &debug); bool success = true; PageSegMode current_psm = GetPageSegMode(); SetPageSegMode(mode); SetVariable("classify_enable_learning", "0"); char* text = GetUTF8Text(); if (debug) { tprintf("Trying to adapt \"%s\" to \"%s\"\n", text, wordstr); } if (text != NULL) { PAGE_RES_IT it(page_res_); WERD_RES* word_res = it.word(); if (word_res != NULL) { word_res->word->set_text(wordstr); } else { success = false; } // Check to see if text matches wordstr. int w = 0; int t = 0; for (t = 0; text[t] != '\0'; ++t) { if (text[t] == '\n' || text[t] == ' ') continue; while (wordstr[w] != '\0' && wordstr[w] == ' ') ++w; if (text[t] != wordstr[w]) break; ++w; } if (text[t] != '\0' || wordstr[w] != '\0') { // No match. delete page_res_; GenericVector boxes; page_res_ = tesseract_->SetupApplyBoxes(boxes, block_list_); tesseract_->ReSegmentByClassification(page_res_); tesseract_->TidyUp(page_res_); PAGE_RES_IT pr_it(page_res_); if (pr_it.word() == NULL) success = false; else word_res = pr_it.word(); } else { word_res->BestChoiceToCorrectText(); } if (success) { tesseract_->EnableLearning = true; tesseract_->LearnWord(NULL, word_res); } delete [] text; } else { success = false; } SetPageSegMode(current_psm); return success; } /** * Free up recognition results and any stored image data, without actually * freeing any recognition data that would be time-consuming to reload. * Afterwards, you must call SetImage or TesseractRect before doing * any Recognize or Get* operation. */ void TessBaseAPI::Clear() { if (thresholder_ != NULL) thresholder_->Clear(); ClearResults(); SetInputImage(NULL); } /** * Close down tesseract and free up all memory. End() is equivalent to * destructing and reconstructing your TessBaseAPI. * Once End() has been used, none of the other API functions may be used * other than Init and anything declared above it in the class definition. */ void TessBaseAPI::End() { if (thresholder_ != NULL) { delete thresholder_; thresholder_ = NULL; } if (page_res_ != NULL) { delete page_res_; page_res_ = NULL; } if (block_list_ != NULL) { delete block_list_; block_list_ = NULL; } if (paragraph_models_ != NULL) { paragraph_models_->delete_data_pointers(); delete paragraph_models_; paragraph_models_ = NULL; } if (tesseract_ != NULL) { delete tesseract_; if (osd_tesseract_ == tesseract_) osd_tesseract_ = NULL; tesseract_ = NULL; } if (osd_tesseract_ != NULL) { delete osd_tesseract_; osd_tesseract_ = NULL; } if (equ_detect_ != NULL) { delete equ_detect_; equ_detect_ = NULL; } if (input_file_ != NULL) { delete input_file_; input_file_ = NULL; } if (input_image_ != NULL) { pixDestroy(&input_image_); input_image_ = NULL; } if (output_file_ != NULL) { delete output_file_; output_file_ = NULL; } if (datapath_ != NULL) { delete datapath_; datapath_ = NULL; } if (language_ != NULL) { delete language_; language_ = NULL; } } // Clear any library-level memory caches. // There are a variety of expensive-to-load constant data structures (mostly // language dictionaries) that are cached globally -- surviving the Init() // and End() of individual TessBaseAPI's. This function allows the clearing // of these caches. void TessBaseAPI::ClearPersistentCache() { Dict::GlobalDawgCache()->DeleteUnusedDawgs(); } /** * Check whether a word is valid according to Tesseract's language model * returns 0 if the word is invalid, non-zero if valid */ int TessBaseAPI::IsValidWord(const char *word) { return tesseract_->getDict().valid_word(word); } // Returns true if utf8_character is defined in the UniCharset. bool TessBaseAPI::IsValidCharacter(const char *utf8_character) { return tesseract_->unicharset.contains_unichar(utf8_character); } // TODO(rays) Obsolete this function and replace with a more aptly named // function that returns image coordinates rather than tesseract coordinates. bool TessBaseAPI::GetTextDirection(int* out_offset, float* out_slope) { PageIterator* it = AnalyseLayout(); if (it == NULL) { return false; } int x1, x2, y1, y2; it->Baseline(RIL_TEXTLINE, &x1, &y1, &x2, &y2); // Calculate offset and slope (NOTE: Kind of ugly) if (x2 <= x1) x2 = x1 + 1; // Convert the point pair to slope/offset of the baseline (in image coords.) *out_slope = static_cast(y2 - y1) / (x2 - x1); *out_offset = static_cast(y1 - *out_slope * x1); // Get the y-coord of the baseline at the left and right edges of the // textline's bounding box. int left, top, right, bottom; if (!it->BoundingBox(RIL_TEXTLINE, &left, &top, &right, &bottom)) { delete it; return false; } int left_y = IntCastRounded(*out_slope * left + *out_offset); int right_y = IntCastRounded(*out_slope * right + *out_offset); // Shift the baseline down so it passes through the nearest bottom-corner // of the textline's bounding box. This is the difference between the y // at the lowest (max) edge of the box and the actual box bottom. *out_offset += bottom - MAX(left_y, right_y); // Switch back to bottom-up tesseract coordinates. Requires negation of // the slope and height - offset for the offset. *out_slope = -*out_slope; *out_offset = rect_height_ - *out_offset; delete it; return true; } /** Sets Dict::letter_is_okay_ function to point to the given function. */ void TessBaseAPI::SetDictFunc(DictFunc f) { if (tesseract_ != NULL) { tesseract_->getDict().letter_is_okay_ = f; } } /** * Sets Dict::probability_in_context_ function to point to the given * function. * * @param f A single function that returns the probability of the current * "character" (in general a utf-8 string), given the context of a previous * utf-8 string. */ void TessBaseAPI::SetProbabilityInContextFunc(ProbabilityInContextFunc f) { if (tesseract_ != NULL) { tesseract_->getDict().probability_in_context_ = f; // Set it for the sublangs too. int num_subs = tesseract_->num_sub_langs(); for (int i = 0; i < num_subs; ++i) { tesseract_->get_sub_lang(i)->getDict().probability_in_context_ = f; } } } /** Sets Wordrec::fill_lattice_ function to point to the given function. */ void TessBaseAPI::SetFillLatticeFunc(FillLatticeFunc f) { if (tesseract_ != NULL) tesseract_->fill_lattice_ = f; } /** Common code for setting the image. */ bool TessBaseAPI::InternalSetImage() { if (tesseract_ == NULL) { tprintf("Please call Init before attempting to set an image."); return false; } if (thresholder_ == NULL) thresholder_ = new ImageThresholder; ClearResults(); return true; } /** * Run the thresholder to make the thresholded image, returned in pix, * which must not be NULL. *pix must be initialized to NULL, or point * to an existing pixDestroyable Pix. * The usual argument to Threshold is Tesseract::mutable_pix_binary(). */ void TessBaseAPI::Threshold(Pix** pix) { ASSERT_HOST(pix != NULL); if (*pix != NULL) pixDestroy(pix); // Zero resolution messes up the algorithms, so make sure it is credible. int y_res = thresholder_->GetScaledYResolution(); if (y_res < kMinCredibleResolution || y_res > kMaxCredibleResolution) { // Use the minimum default resolution, as it is safer to under-estimate // than over-estimate resolution. thresholder_->SetSourceYResolution(kMinCredibleResolution); } PageSegMode pageseg_mode = static_cast( static_cast(tesseract_->tessedit_pageseg_mode)); thresholder_->ThresholdToPix(pageseg_mode, pix); thresholder_->GetImageSizes(&rect_left_, &rect_top_, &rect_width_, &rect_height_, &image_width_, &image_height_); if (!thresholder_->IsBinary()) { tesseract_->set_pix_thresholds(thresholder_->GetPixRectThresholds()); tesseract_->set_pix_grey(thresholder_->GetPixRectGrey()); } else { tesseract_->set_pix_thresholds(NULL); tesseract_->set_pix_grey(NULL); } // Set the internal resolution that is used for layout parameters from the // estimated resolution, rather than the image resolution, which may be // fabricated, but we will use the image resolution, if there is one, to // report output point sizes. int estimated_res = ClipToRange(thresholder_->GetScaledEstimatedResolution(), kMinCredibleResolution, kMaxCredibleResolution); if (estimated_res != thresholder_->GetScaledEstimatedResolution()) { tprintf("Estimated resolution %d out of range! Corrected to %d\n", thresholder_->GetScaledEstimatedResolution(), estimated_res); } tesseract_->set_source_resolution(estimated_res); SavePixForCrash(estimated_res, *pix); } /** Find lines from the image making the BLOCK_LIST. */ int TessBaseAPI::FindLines() { if (thresholder_ == NULL || thresholder_->IsEmpty()) { tprintf("Please call SetImage before attempting recognition."); return -1; } if (recognition_done_) ClearResults(); if (!block_list_->empty()) { return 0; } if (tesseract_ == NULL) { tesseract_ = new Tesseract; tesseract_->InitAdaptiveClassifier(false); } if (tesseract_->pix_binary() == NULL) Threshold(tesseract_->mutable_pix_binary()); if (tesseract_->ImageWidth() > MAX_INT16 || tesseract_->ImageHeight() > MAX_INT16) { tprintf("Image too large: (%d, %d)\n", tesseract_->ImageWidth(), tesseract_->ImageHeight()); return -1; } tesseract_->PrepareForPageseg(); if (tesseract_->textord_equation_detect) { if (equ_detect_ == NULL && datapath_ != NULL) { equ_detect_ = new EquationDetect(datapath_->string(), NULL); } tesseract_->SetEquationDetect(equ_detect_); } Tesseract* osd_tess = osd_tesseract_; OSResults osr; if (PSM_OSD_ENABLED(tesseract_->tessedit_pageseg_mode) && osd_tess == NULL) { if (strcmp(language_->string(), "osd") == 0) { osd_tess = tesseract_; } else { osd_tesseract_ = new Tesseract; if (osd_tesseract_->init_tesseract( datapath_->string(), NULL, "osd", OEM_TESSERACT_ONLY, NULL, 0, NULL, NULL, false) == 0) { osd_tess = osd_tesseract_; osd_tesseract_->set_source_resolution( thresholder_->GetSourceYResolution()); } else { tprintf("Warning: Auto orientation and script detection requested," " but osd language failed to load\n"); delete osd_tesseract_; osd_tesseract_ = NULL; } } } if (tesseract_->SegmentPage(input_file_, block_list_, osd_tess, &osr) < 0) return -1; // If Devanagari is being recognized, we use different images for page seg // and for OCR. tesseract_->PrepareForTessOCR(block_list_, osd_tess, &osr); return 0; } /** Delete the pageres and clear the block list ready for a new page. */ void TessBaseAPI::ClearResults() { if (tesseract_ != NULL) { tesseract_->Clear(); } if (page_res_ != NULL) { delete page_res_; page_res_ = NULL; } recognition_done_ = false; if (block_list_ == NULL) block_list_ = new BLOCK_LIST; else block_list_->clear(); if (paragraph_models_ != NULL) { paragraph_models_->delete_data_pointers(); delete paragraph_models_; paragraph_models_ = NULL; } SavePixForCrash(0, NULL); } /** * Return the length of the output text string, as UTF8, assuming * liberally two spacing marks after each word (as paragraphs end with two * newlines), and assuming a single character reject marker for each rejected * character. * Also return the number of recognized blobs in blob_count. */ int TessBaseAPI::TextLength(int* blob_count) { if (tesseract_ == NULL || page_res_ == NULL) return 0; PAGE_RES_IT page_res_it(page_res_); int total_length = 2; int total_blobs = 0; // Iterate over the data structures to extract the recognition result. for (page_res_it.restart_page(); page_res_it.word () != NULL; page_res_it.forward()) { WERD_RES *word = page_res_it.word(); WERD_CHOICE* choice = word->best_choice; if (choice != NULL) { total_blobs += choice->length() + 2; total_length += choice->unichar_string().length() + 2; for (int i = 0; i < word->reject_map.length(); ++i) { if (word->reject_map[i].rejected()) ++total_length; } } } if (blob_count != NULL) *blob_count = total_blobs; return total_length; } /** * Estimates the Orientation And Script of the image. * Returns true if the image was processed successfully. */ bool TessBaseAPI::DetectOS(OSResults* osr) { if (tesseract_ == NULL) return false; ClearResults(); if (tesseract_->pix_binary() == NULL) Threshold(tesseract_->mutable_pix_binary()); if (input_file_ == NULL) input_file_ = new STRING(kInputFile); return orientation_and_script_detection(*input_file_, osr, tesseract_); } void TessBaseAPI::set_min_orientation_margin(double margin) { tesseract_->min_orientation_margin.set_value(margin); } /** * Return text orientation of each block as determined in an earlier page layout * analysis operation. Orientation is returned as the number of ccw 90-degree * rotations (in [0..3]) required to make the text in the block upright * (readable). Note that this may not necessary be the block orientation * preferred for recognition (such as the case of vertical CJK text). * * Also returns whether the text in the block is believed to have vertical * writing direction (when in an upright page orientation). * * The returned array is of length equal to the number of text blocks, which may * be less than the total number of blocks. The ordering is intended to be * consistent with GetTextLines(). */ void TessBaseAPI::GetBlockTextOrientations(int** block_orientation, bool** vertical_writing) { delete[] *block_orientation; *block_orientation = NULL; delete[] *vertical_writing; *vertical_writing = NULL; BLOCK_IT block_it(block_list_); block_it.move_to_first(); int num_blocks = 0; for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) { if (!block_it.data()->poly_block()->IsText()) { continue; } ++num_blocks; } if (!num_blocks) { tprintf("WARNING: Found no blocks\n"); return; } *block_orientation = new int[num_blocks]; *vertical_writing = new bool[num_blocks]; block_it.move_to_first(); int i = 0; for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) { if (!block_it.data()->poly_block()->IsText()) { continue; } FCOORD re_rotation = block_it.data()->re_rotation(); float re_theta = re_rotation.angle(); FCOORD classify_rotation = block_it.data()->classify_rotation(); float classify_theta = classify_rotation.angle(); double rot_theta = - (re_theta - classify_theta) * 2.0 / PI; if (rot_theta < 0) rot_theta += 4; int num_rotations = static_cast(rot_theta + 0.5); (*block_orientation)[i] = num_rotations; // The classify_rotation is non-zero only if the text has vertical // writing direction. (*vertical_writing)[i] = classify_rotation.y() != 0.0f; ++i; } } // ____________________________________________________________________________ // Ocropus add-ons. /** Find lines from the image making the BLOCK_LIST. */ BLOCK_LIST* TessBaseAPI::FindLinesCreateBlockList() { FindLines(); BLOCK_LIST* result = block_list_; block_list_ = NULL; return result; } /** * Delete a block list. * This is to keep BLOCK_LIST pointer opaque * and let go of including the other headers. */ void TessBaseAPI::DeleteBlockList(BLOCK_LIST *block_list) { delete block_list; } ROW *TessBaseAPI::MakeTessOCRRow(float baseline, float xheight, float descender, float ascender) { inT32 xstarts[] = {-32000}; double quad_coeffs[] = {0, 0, baseline}; return new ROW(1, xstarts, quad_coeffs, xheight, ascender - (baseline + xheight), descender - baseline, 0, 0); } /** Creates a TBLOB* from the whole pix. */ TBLOB *TessBaseAPI::MakeTBLOB(Pix *pix) { int width = pixGetWidth(pix); int height = pixGetHeight(pix); BLOCK block("a character", TRUE, 0, 0, 0, 0, width, height); // Create C_BLOBs from the page extract_edges(pix, &block); // Merge all C_BLOBs C_BLOB_LIST *list = block.blob_list(); C_BLOB_IT c_blob_it(list); if (c_blob_it.empty()) return NULL; // Move all the outlines to the first blob. C_OUTLINE_IT ol_it(c_blob_it.data()->out_list()); for (c_blob_it.forward(); !c_blob_it.at_first(); c_blob_it.forward()) { C_BLOB *c_blob = c_blob_it.data(); ol_it.add_list_after(c_blob->out_list()); } // Convert the first blob to the output TBLOB. return TBLOB::PolygonalCopy(false, c_blob_it.data()); } /** * This method baseline normalizes a TBLOB in-place. The input row is used * for normalization. The denorm is an optional parameter in which the * normalization-antidote is returned. */ void TessBaseAPI::NormalizeTBLOB(TBLOB *tblob, ROW *row, bool numeric_mode) { TBOX box = tblob->bounding_box(); float x_center = (box.left() + box.right()) / 2.0f; float baseline = row->base_line(x_center); float scale = kBlnXHeight / row->x_height(); tblob->Normalize(NULL, NULL, NULL, x_center, baseline, scale, scale, 0.0f, static_cast(kBlnBaselineOffset), false, NULL); } /** * Return a TBLOB * from the whole pix. * To be freed later with delete. */ TBLOB *make_tesseract_blob(float baseline, float xheight, float descender, float ascender, bool numeric_mode, Pix* pix) { TBLOB *tblob = TessBaseAPI::MakeTBLOB(pix); // Normalize TBLOB ROW *row = TessBaseAPI::MakeTessOCRRow(baseline, xheight, descender, ascender); TessBaseAPI::NormalizeTBLOB(tblob, row, numeric_mode); delete row; return tblob; } /** * Adapt to recognize the current image as the given character. * The image must be preloaded into pix_binary_ and be just an image * of a single character. */ void TessBaseAPI::AdaptToCharacter(const char *unichar_repr, int length, float baseline, float xheight, float descender, float ascender) { UNICHAR_ID id = tesseract_->unicharset.unichar_to_id(unichar_repr, length); TBLOB *blob = make_tesseract_blob(baseline, xheight, descender, ascender, tesseract_->classify_bln_numeric_mode, tesseract_->pix_binary()); float threshold; float best_rating = -100; // Classify to get a raw choice. BLOB_CHOICE_LIST choices; tesseract_->AdaptiveClassifier(blob, &choices); BLOB_CHOICE_IT choice_it; choice_it.set_to_list(&choices); for (choice_it.mark_cycle_pt(); !choice_it.cycled_list(); choice_it.forward()) { if (choice_it.data()->rating() > best_rating) { best_rating = choice_it.data()->rating(); } } threshold = tesseract_->matcher_good_threshold; if (blob->outlines) tesseract_->AdaptToChar(blob, id, kUnknownFontinfoId, threshold, tesseract_->AdaptedTemplates); delete blob; } PAGE_RES* TessBaseAPI::RecognitionPass1(BLOCK_LIST* block_list) { PAGE_RES *page_res = new PAGE_RES(false, block_list, &(tesseract_->prev_word_best_choice_)); tesseract_->recog_all_words(page_res, NULL, NULL, NULL, 1); return page_res; } PAGE_RES* TessBaseAPI::RecognitionPass2(BLOCK_LIST* block_list, PAGE_RES* pass1_result) { if (!pass1_result) pass1_result = new PAGE_RES(false, block_list, &(tesseract_->prev_word_best_choice_)); tesseract_->recog_all_words(pass1_result, NULL, NULL, NULL, 2); return pass1_result; } void TessBaseAPI::DetectParagraphs(bool after_text_recognition) { int debug_level = 0; GetIntVariable("paragraph_debug_level", &debug_level); if (paragraph_models_ == NULL) paragraph_models_ = new GenericVector; MutableIterator *result_it = GetMutableIterator(); do { // Detect paragraphs for this block GenericVector models; ::tesseract::DetectParagraphs(debug_level, after_text_recognition, result_it, &models); *paragraph_models_ += models; } while (result_it->Next(RIL_BLOCK)); delete result_it; } struct TESS_CHAR : ELIST_LINK { char *unicode_repr; int length; // of unicode_repr float cost; TBOX box; TESS_CHAR(float _cost, const char *repr, int len = -1) : cost(_cost) { length = (len == -1 ? strlen(repr) : len); unicode_repr = new char[length + 1]; strncpy(unicode_repr, repr, length); } TESS_CHAR() { // Satisfies ELISTIZE. } ~TESS_CHAR() { delete [] unicode_repr; } }; ELISTIZEH(TESS_CHAR) ELISTIZE(TESS_CHAR) static void add_space(TESS_CHAR_IT* it) { TESS_CHAR *t = new TESS_CHAR(0, " "); it->add_after_then_move(t); } static float rating_to_cost(float rating) { rating = 100 + rating; // cuddled that to save from coverage profiler // (I have never seen ratings worse than -100, // but the check won't hurt) if (rating < 0) rating = 0; return rating; } /** * Extract the OCR results, costs (penalty points for uncertainty), * and the bounding boxes of the characters. */ static void extract_result(TESS_CHAR_IT* out, PAGE_RES* page_res) { PAGE_RES_IT page_res_it(page_res); int word_count = 0; while (page_res_it.word() != NULL) { WERD_RES *word = page_res_it.word(); const char *str = word->best_choice->unichar_string().string(); const char *len = word->best_choice->unichar_lengths().string(); TBOX real_rect = word->word->bounding_box(); if (word_count) add_space(out); int n = strlen(len); for (int i = 0; i < n; i++) { TESS_CHAR *tc = new TESS_CHAR(rating_to_cost(word->best_choice->rating()), str, *len); tc->box = real_rect.intersection(word->box_word->BlobBox(i)); out->add_after_then_move(tc); str += *len; len++; } page_res_it.forward(); word_count++; } } /** * Extract the OCR results, costs (penalty points for uncertainty), * and the bounding boxes of the characters. */ int TessBaseAPI::TesseractExtractResult(char** text, int** lengths, float** costs, int** x0, int** y0, int** x1, int** y1, PAGE_RES* page_res) { TESS_CHAR_LIST tess_chars; TESS_CHAR_IT tess_chars_it(&tess_chars); extract_result(&tess_chars_it, page_res); tess_chars_it.move_to_first(); int n = tess_chars.length(); int text_len = 0; *lengths = new int[n]; *costs = new float[n]; *x0 = new int[n]; *y0 = new int[n]; *x1 = new int[n]; *y1 = new int[n]; int i = 0; for (tess_chars_it.mark_cycle_pt(); !tess_chars_it.cycled_list(); tess_chars_it.forward(), i++) { TESS_CHAR *tc = tess_chars_it.data(); text_len += (*lengths)[i] = tc->length; (*costs)[i] = tc->cost; (*x0)[i] = tc->box.left(); (*y0)[i] = tc->box.bottom(); (*x1)[i] = tc->box.right(); (*y1)[i] = tc->box.top(); } char *p = *text = new char[text_len]; tess_chars_it.move_to_first(); for (tess_chars_it.mark_cycle_pt(); !tess_chars_it.cycled_list(); tess_chars_it.forward()) { TESS_CHAR *tc = tess_chars_it.data(); strncpy(p, tc->unicode_repr, tc->length); p += tc->length; } return n; } /** This method returns the features associated with the input blob. */ // The resulting features are returned in int_features, which must be // of size MAX_NUM_INT_FEATURES. The number of features is returned in // num_features (or 0 if there was a failure). // On return feature_outline_index is filled with an index of the outline // corresponding to each feature in int_features. // TODO(rays) Fix the caller to out outline_counts instead. void TessBaseAPI::GetFeaturesForBlob(TBLOB* blob, INT_FEATURE_STRUCT* int_features, int* num_features, int* feature_outline_index) { GenericVector outline_counts; GenericVector bl_features; GenericVector cn_features; INT_FX_RESULT_STRUCT fx_info; tesseract_->ExtractFeatures(*blob, false, &bl_features, &cn_features, &fx_info, &outline_counts); if (cn_features.size() == 0 || cn_features.size() > MAX_NUM_INT_FEATURES) { *num_features = 0; return; // Feature extraction failed. } *num_features = cn_features.size(); memcpy(int_features, &cn_features[0], *num_features * sizeof(cn_features[0])); // TODO(rays) Pass outline_counts back and simplify the calling code. if (feature_outline_index != NULL) { int f = 0; for (int i = 0; i < outline_counts.size(); ++i) { while (f < outline_counts[i]) feature_outline_index[f++] = i; } } } // This method returns the row to which a box of specified dimensions would // belong. If no good match is found, it returns NULL. ROW* TessBaseAPI::FindRowForBox(BLOCK_LIST* blocks, int left, int top, int right, int bottom) { TBOX box(left, bottom, right, top); BLOCK_IT b_it(blocks); for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { BLOCK* block = b_it.data(); if (!box.major_overlap(block->bounding_box())) continue; ROW_IT r_it(block->row_list()); for (r_it.mark_cycle_pt(); !r_it.cycled_list(); r_it.forward()) { ROW* row = r_it.data(); if (!box.major_overlap(row->bounding_box())) continue; WERD_IT w_it(row->word_list()); for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) { WERD* word = w_it.data(); if (box.major_overlap(word->bounding_box())) return row; } } } return NULL; } /** Method to run adaptive classifier on a blob. */ void TessBaseAPI::RunAdaptiveClassifier(TBLOB* blob, int num_max_matches, int* unichar_ids, float* ratings, int* num_matches_returned) { BLOB_CHOICE_LIST* choices = new BLOB_CHOICE_LIST; tesseract_->AdaptiveClassifier(blob, choices); BLOB_CHOICE_IT choices_it(choices); int& index = *num_matches_returned; index = 0; for (choices_it.mark_cycle_pt(); !choices_it.cycled_list() && index < num_max_matches; choices_it.forward()) { BLOB_CHOICE* choice = choices_it.data(); unichar_ids[index] = choice->unichar_id(); ratings[index] = choice->rating(); ++index; } *num_matches_returned = index; delete choices; } /** This method returns the string form of the specified unichar. */ const char* TessBaseAPI::GetUnichar(int unichar_id) { return tesseract_->unicharset.id_to_unichar(unichar_id); } /** Return the pointer to the i-th dawg loaded into tesseract_ object. */ const Dawg *TessBaseAPI::GetDawg(int i) const { if (tesseract_ == NULL || i >= NumDawgs()) return NULL; return tesseract_->getDict().GetDawg(i); } /** Return the number of dawgs loaded into tesseract_ object. */ int TessBaseAPI::NumDawgs() const { return tesseract_ == NULL ? 0 : tesseract_->getDict().NumDawgs(); } #ifndef NO_CUBE_BUILD /** Return a pointer to underlying CubeRecoContext object if present. */ CubeRecoContext *TessBaseAPI::GetCubeRecoContext() const { return (tesseract_ == NULL) ? NULL : tesseract_->GetCubeRecoContext(); } #endif // NO_CUBE_BUILD /** Escape a char string - remove <>&"' with HTML codes. */ STRING HOcrEscape(const char* text) { STRING ret; const char *ptr; for (ptr = text; *ptr; ptr++) { switch (*ptr) { case '<': ret += "<"; break; case '>': ret += ">"; break; case '&': ret += "&"; break; case '"': ret += """; break; case '\'': ret += "'"; break; default: ret += *ptr; } } return ret; } } // namespace tesseract. tesseract-3.04.01/api/baseapi.h000066400000000000000000001046511266071204500162140ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: baseapi.h // Description: Simple API for calling tesseract. // Author: Ray Smith // Created: Fri Oct 06 15:35:01 PDT 2006 // // (C) Copyright 2006, 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_API_BASEAPI_H__ #define TESSERACT_API_BASEAPI_H__ #define TESSERACT_VERSION_STR "3.04.01" #define TESSERACT_VERSION 0x030401 #define MAKE_VERSION(major, minor, patch) (((major) << 16) | ((minor) << 8) | \ (patch)) #include // To avoid collision with other typenames include the ABSOLUTE MINIMUM // complexity of includes here. Use forward declarations wherever possible // and hide includes of complex types in baseapi.cpp. #include "platform.h" #include "apitypes.h" #include "thresholder.h" #include "unichar.h" #include "tesscallback.h" #include "publictypes.h" #include "pageiterator.h" #include "resultiterator.h" template class GenericVector; class PAGE_RES; class PAGE_RES_IT; class ParagraphModel; struct BlamerBundle; class BLOCK_LIST; class DENORM; class MATRIX; class ROW; class STRING; class WERD; struct Pix; struct Box; struct Pixa; struct Boxa; class ETEXT_DESC; struct OSResults; class TBOX; class UNICHARSET; class WERD_CHOICE_LIST; struct INT_FEATURE_STRUCT; typedef INT_FEATURE_STRUCT *INT_FEATURE; struct TBLOB; namespace tesseract { #ifndef NO_CUBE_BUILD class CubeRecoContext; #endif // NO_CUBE_BUILD class Dawg; class Dict; class EquationDetect; class PageIterator; class LTRResultIterator; class ResultIterator; class MutableIterator; class TessResultRenderer; class Tesseract; class Trie; class Wordrec; typedef int (Dict::*DictFunc)(void* void_dawg_args, UNICHAR_ID unichar_id, bool word_end) const; typedef double (Dict::*ProbabilityInContextFunc)(const char* lang, const char* context, int context_bytes, const char* character, int character_bytes); typedef float (Dict::*ParamsModelClassifyFunc)( const char *lang, void *path); typedef void (Wordrec::*FillLatticeFunc)(const MATRIX &ratings, const WERD_CHOICE_LIST &best_choices, const UNICHARSET &unicharset, BlamerBundle *blamer_bundle); typedef TessCallback4 TruthCallback; /** * Base class for all tesseract APIs. * Specific classes can add ability to work on different inputs or produce * different outputs. * This class is mostly an interface layer on top of the Tesseract instance * class to hide the data types so that users of this class don't have to * include any other Tesseract headers. */ class TESS_API TessBaseAPI { public: TessBaseAPI(); virtual ~TessBaseAPI(); /** * Returns the version identifier as a static string. Do not delete. */ static const char* Version(); /** * If compiled with OpenCL AND an available OpenCL * device is deemed faster than serial code, then * "device" is populated with the cl_device_id * and returns sizeof(cl_device_id) * otherwise *device=NULL and returns 0. */ static size_t getOpenCLDevice(void **device); /** * Writes the thresholded image to stderr as a PBM file on receipt of a * SIGSEGV, SIGFPE, or SIGBUS signal. (Linux/Unix only). */ static void CatchSignals(); /** * Set the name of the input file. Needed for training and * reading a UNLV zone file, and for searchable PDF output. */ void SetInputName(const char* name); /** * These functions are required for searchable PDF output. * We need our hands on the input file so that we can include * it in the PDF without transcoding. If that is not possible, * we need the original image. Finally, resolution metadata * is stored in the PDF so we need that as well. */ const char* GetInputName(); void SetInputImage(Pix *pix); Pix* GetInputImage(); int GetSourceYResolution(); const char* GetDatapath(); /** Set the name of the bonus output files. Needed only for debugging. */ void SetOutputName(const char* name); /** * Set the value of an internal "parameter." * Supply the name of the parameter and the value as a string, just as * you would in a config file. * Returns false if the name lookup failed. * Eg SetVariable("tessedit_char_blacklist", "xyz"); to ignore x, y and z. * Or SetVariable("classify_bln_numeric_mode", "1"); to set numeric-only mode. * SetVariable may be used before Init, but settings will revert to * defaults on End(). * * Note: Must be called after Init(). Only works for non-init variables * (init variables should be passed to Init()). */ bool SetVariable(const char* name, const char* value); bool SetDebugVariable(const char* name, const char* value); /** * Returns true if the parameter was found among Tesseract parameters. * Fills in value with the value of the parameter. */ bool GetIntVariable(const char *name, int *value) const; bool GetBoolVariable(const char *name, bool *value) const; bool GetDoubleVariable(const char *name, double *value) const; /** * Returns the pointer to the string that represents the value of the * parameter if it was found among Tesseract parameters. */ const char *GetStringVariable(const char *name) const; /** * Print Tesseract parameters to the given file. */ void PrintVariables(FILE *fp) const; /** * Get value of named variable as a string, if it exists. */ bool GetVariableAsString(const char *name, STRING *val); /** * Instances are now mostly thread-safe and totally independent, * but some global parameters remain. Basically it is safe to use multiple * TessBaseAPIs in different threads in parallel, UNLESS: * you use SetVariable on some of the Params in classify and textord. * If you do, then the effect will be to change it for all your instances. * * Start tesseract. Returns zero on success and -1 on failure. * NOTE that the only members that may be called before Init are those * listed above here in the class definition. * * The datapath must be the name of the parent directory of tessdata and * must end in / . Any name after the last / will be stripped. * The language is (usually) an ISO 639-3 string or NULL will default to eng. * It is entirely safe (and eventually will be efficient too) to call * Init multiple times on the same instance to change language, or just * to reset the classifier. * The language may be a string of the form [~][+[~]]* indicating * that multiple languages are to be loaded. Eg hin+eng will load Hindi and * English. Languages may specify internally that they want to be loaded * with one or more other languages, so the ~ sign is available to override * that. Eg if hin were set to load eng by default, then hin+~eng would force * loading only hin. The number of loaded languages is limited only by * memory, with the caveat that loading additional languages will impact * both speed and accuracy, as there is more work to do to decide on the * applicable language, and there is more chance of hallucinating incorrect * words. * WARNING: On changing languages, all Tesseract parameters are reset * back to their default values. (Which may vary between languages.) * If you have a rare need to set a Variable that controls * initialization for a second call to Init you should explicitly * call End() and then use SetVariable before Init. This is only a very * rare use case, since there are very few uses that require any parameters * to be set before Init. * * If set_only_non_debug_params is true, only params that do not contain * "debug" in the name will be set. */ int Init(const char* datapath, const char* language, OcrEngineMode mode, char **configs, int configs_size, const GenericVector *vars_vec, const GenericVector *vars_values, bool set_only_non_debug_params); int Init(const char* datapath, const char* language, OcrEngineMode oem) { return Init(datapath, language, oem, NULL, 0, NULL, NULL, false); } int Init(const char* datapath, const char* language) { return Init(datapath, language, OEM_DEFAULT, NULL, 0, NULL, NULL, false); } /** * Returns the languages string used in the last valid initialization. * If the last initialization specified "deu+hin" then that will be * returned. If hin loaded eng automatically as well, then that will * not be included in this list. To find the languages actually * loaded use GetLoadedLanguagesAsVector. * The returned string should NOT be deleted. */ const char* GetInitLanguagesAsString() const; /** * Returns the loaded languages in the vector of STRINGs. * Includes all languages loaded by the last Init, including those loaded * as dependencies of other loaded languages. */ void GetLoadedLanguagesAsVector(GenericVector* langs) const; /** * Returns the available languages in the vector of STRINGs. */ void GetAvailableLanguagesAsVector(GenericVector* langs) const; /** * Init only the lang model component of Tesseract. The only functions * that work after this init are SetVariable and IsValidWord. * WARNING: temporary! This function will be removed from here and placed * in a separate API at some future time. */ int InitLangMod(const char* datapath, const char* language); /** * Init only for page layout analysis. Use only for calls to SetImage and * AnalysePage. Calls that attempt recognition will generate an error. */ void InitForAnalysePage(); /** * Read a "config" file containing a set of param, value pairs. * Searches the standard places: tessdata/configs, tessdata/tessconfigs * and also accepts a relative or absolute path name. * Note: only non-init params will be set (init params are set by Init()). */ void ReadConfigFile(const char* filename); /** Same as above, but only set debug params from the given config file. */ void ReadDebugConfigFile(const char* filename); /** * Set the current page segmentation mode. Defaults to PSM_SINGLE_BLOCK. * The mode is stored as an IntParam so it can also be modified by * ReadConfigFile or SetVariable("tessedit_pageseg_mode", mode as string). */ void SetPageSegMode(PageSegMode mode); /** Return the current page segmentation mode. */ PageSegMode GetPageSegMode() const; /** * Recognize a rectangle from an image and return the result as a string. * May be called many times for a single Init. * Currently has no error checking. * Greyscale of 8 and color of 24 or 32 bits per pixel may be given. * Palette color images will not work properly and must be converted to * 24 bit. * Binary images of 1 bit per pixel may also be given but they must be * byte packed with the MSB of the first byte being the first pixel, and a * 1 represents WHITE. For binary images set bytes_per_pixel=0. * The recognized text is returned as a char* which is coded * as UTF8 and must be freed with the delete [] operator. * * Note that TesseractRect is the simplified convenience interface. * For advanced uses, use SetImage, (optionally) SetRectangle, Recognize, * and one or more of the Get*Text functions below. */ char* TesseractRect(const unsigned char* imagedata, int bytes_per_pixel, int bytes_per_line, int left, int top, int width, int height); /** * Call between pages or documents etc to free up memory and forget * adaptive data. */ void ClearAdaptiveClassifier(); /** * @defgroup AdvancedAPI Advanced API * The following methods break TesseractRect into pieces, so you can * get hold of the thresholded image, get the text in different formats, * get bounding boxes, confidences etc. */ /* @{ */ /** * Provide an image for Tesseract to recognize. Format is as * TesseractRect above. Does not copy the image buffer, or take * ownership. The source image may be destroyed after Recognize is called, * either explicitly or implicitly via one of the Get*Text functions. * SetImage clears all recognition results, and sets the rectangle to the * full image, so it may be followed immediately by a GetUTF8Text, and it * will automatically perform recognition. */ void SetImage(const unsigned char* imagedata, int width, int height, int bytes_per_pixel, int bytes_per_line); /** * Provide an image for Tesseract to recognize. As with SetImage above, * Tesseract doesn't take a copy or ownership or pixDestroy the image, so * it must persist until after Recognize. * Pix vs raw, which to use? * Use Pix where possible. A future version of Tesseract may choose to use Pix * as its internal representation and discard IMAGE altogether. * Because of that, an implementation that sources and targets Pix may end up * with less copies than an implementation that does not. */ void SetImage(Pix* pix); /** * Set the resolution of the source image in pixels per inch so font size * information can be calculated in results. Call this after SetImage(). */ void SetSourceResolution(int ppi); /** * Restrict recognition to a sub-rectangle of the image. Call after SetImage. * Each SetRectangle clears the recogntion results so multiple rectangles * can be recognized with the same image. */ void SetRectangle(int left, int top, int width, int height); /** * In extreme cases only, usually with a subclass of Thresholder, it * is possible to provide a different Thresholder. The Thresholder may * be preloaded with an image, settings etc, or they may be set after. * Note that Tesseract takes ownership of the Thresholder and will * delete it when it it is replaced or the API is destructed. */ void SetThresholder(ImageThresholder* thresholder) { if (thresholder_ != NULL) delete thresholder_; thresholder_ = thresholder; ClearResults(); } /** * Get a copy of the internal thresholded image from Tesseract. * Caller takes ownership of the Pix and must pixDestroy it. * May be called any time after SetImage, or after TesseractRect. */ Pix* GetThresholdedImage(); /** * Get the result of page layout analysis as a leptonica-style * Boxa, Pixa pair, in reading order. * Can be called before or after Recognize. */ Boxa* GetRegions(Pixa** pixa); /** * Get the textlines as a leptonica-style * Boxa, Pixa pair, in reading order. * Can be called before or after Recognize. * If raw_image is true, then extract from the original image instead of the * thresholded image and pad by raw_padding pixels. * If blockids is not NULL, the block-id of each line is also returned as an * array of one element per line. delete [] after use. * If paraids is not NULL, the paragraph-id of each line within its block is * also returned as an array of one element per line. delete [] after use. */ Boxa* GetTextlines(const bool raw_image, const int raw_padding, Pixa** pixa, int** blockids, int** paraids); /* Helper method to extract from the thresholded image. (most common usage) */ Boxa* GetTextlines(Pixa** pixa, int** blockids) { return GetTextlines(false, 0, pixa, blockids, NULL); } /** * Get textlines and strips of image regions as a leptonica-style Boxa, Pixa * pair, in reading order. Enables downstream handling of non-rectangular * regions. * Can be called before or after Recognize. * If blockids is not NULL, the block-id of each line is also returned as an * array of one element per line. delete [] after use. */ Boxa* GetStrips(Pixa** pixa, int** blockids); /** * Get the words as a leptonica-style * Boxa, Pixa pair, in reading order. * Can be called before or after Recognize. */ Boxa* GetWords(Pixa** pixa); /** * Gets the individual connected (text) components (created * after pages segmentation step, but before recognition) * as a leptonica-style Boxa, Pixa pair, in reading order. * Can be called before or after Recognize. * Note: the caller is responsible for calling boxaDestroy() * on the returned Boxa array and pixaDestroy() on cc array. */ Boxa* GetConnectedComponents(Pixa** cc); /** * Get the given level kind of components (block, textline, word etc.) as a * leptonica-style Boxa, Pixa pair, in reading order. * Can be called before or after Recognize. * If blockids is not NULL, the block-id of each component is also returned * as an array of one element per component. delete [] after use. * If blockids is not NULL, the paragraph-id of each component with its block * is also returned as an array of one element per component. delete [] after * use. * If raw_image is true, then portions of the original image are extracted * instead of the thresholded image and padded with raw_padding. * If text_only is true, then only text components are returned. */ Boxa* GetComponentImages(const PageIteratorLevel level, const bool text_only, const bool raw_image, const int raw_padding, Pixa** pixa, int** blockids, int** paraids); // Helper function to get binary images with no padding (most common usage). Boxa* GetComponentImages(const PageIteratorLevel level, const bool text_only, Pixa** pixa, int** blockids) { return GetComponentImages(level, text_only, false, 0, pixa, blockids, NULL); } /** * Returns the scale factor of the thresholded image that would be returned by * GetThresholdedImage() and the various GetX() methods that call * GetComponentImages(). * Returns 0 if no thresholder has been set. */ int GetThresholdedImageScaleFactor() const; /** * Dump the internal binary image to a PGM file. * @deprecated Use GetThresholdedImage and write the image using pixWrite * instead if possible. */ void DumpPGM(const char* filename); /** * Runs page layout analysis in the mode set by SetPageSegMode. * May optionally be called prior to Recognize to get access to just * the page layout results. Returns an iterator to the results. * If merge_similar_words is true, words are combined where suitable for use * with a line recognizer. Use if you want to use AnalyseLayout to find the * textlines, and then want to process textline fragments with an external * line recognizer. * Returns NULL on error or an empty page. * The returned iterator must be deleted after use. * WARNING! This class points to data held within the TessBaseAPI class, and * therefore can only be used while the TessBaseAPI class still exists and * has not been subjected to a call of Init, SetImage, Recognize, Clear, End * DetectOS, or anything else that changes the internal PAGE_RES. */ PageIterator* AnalyseLayout() { return AnalyseLayout(false); } PageIterator* AnalyseLayout(bool merge_similar_words); /** * Recognize the image from SetAndThresholdImage, generating Tesseract * internal structures. Returns 0 on success. * Optional. The Get*Text functions below will call Recognize if needed. * After Recognize, the output is kept internally until the next SetImage. */ int Recognize(ETEXT_DESC* monitor); /** * Methods to retrieve information after SetAndThresholdImage(), * Recognize() or TesseractRect(). (Recognize is called implicitly if needed.) */ /** Variant on Recognize used for testing chopper. */ int RecognizeForChopTest(ETEXT_DESC* monitor); /** * Turns images into symbolic text. * * filename can point to a single image, a multi-page TIFF, * or a plain text list of image filenames. * * retry_config is useful for debugging. If not NULL, you can fall * back to an alternate configuration if a page fails for some * reason. * * timeout_millisec terminates processing if any single page * takes too long. Set to 0 for unlimited time. * * renderer is responible for creating the output. For example, * use the TessTextRenderer if you want plaintext output, or * the TessPDFRender to produce searchable PDF. * * If tessedit_page_number is non-negative, will only process that * single page. Works for multi-page tiff file, or filelist. * * Returns true if successful, false on error. */ bool ProcessPages(const char* filename, const char* retry_config, int timeout_millisec, TessResultRenderer* renderer); // Does the real work of ProcessPages. bool ProcessPagesInternal(const char* filename, const char* retry_config, int timeout_millisec, TessResultRenderer* renderer); /** * Turn a single image into symbolic text. * * The pix is the image processed. filename and page_index are * metadata used by side-effect processes, such as reading a box * file or formatting as hOCR. * * See ProcessPages for desciptions of other parameters. */ bool ProcessPage(Pix* pix, int page_index, const char* filename, const char* retry_config, int timeout_millisec, TessResultRenderer* renderer); /** * Get a reading-order iterator to the results of LayoutAnalysis and/or * Recognize. The returned iterator must be deleted after use. * WARNING! This class points to data held within the TessBaseAPI class, and * therefore can only be used while the TessBaseAPI class still exists and * has not been subjected to a call of Init, SetImage, Recognize, Clear, End * DetectOS, or anything else that changes the internal PAGE_RES. */ ResultIterator* GetIterator(); /** * Get a mutable iterator to the results of LayoutAnalysis and/or Recognize. * The returned iterator must be deleted after use. * WARNING! This class points to data held within the TessBaseAPI class, and * therefore can only be used while the TessBaseAPI class still exists and * has not been subjected to a call of Init, SetImage, Recognize, Clear, End * DetectOS, or anything else that changes the internal PAGE_RES. */ MutableIterator* GetMutableIterator(); /** * The recognized text is returned as a char* which is coded * as UTF8 and must be freed with the delete [] operator. */ char* GetUTF8Text(); /** * Make a HTML-formatted string with hOCR markup from the internal * data structures. * page_number is 0-based but will appear in the output as 1-based. */ char* GetHOCRText(int page_number); /** * The recognized text is returned as a char* which is coded in the same * format as a box file used in training. Returned string must be freed with * the delete [] operator. * Constructs coordinates in the original image - not just the rectangle. * page_number is a 0-based page index that will appear in the box file. */ char* GetBoxText(int page_number); /** * The recognized text is returned as a char* which is coded * as UNLV format Latin-1 with specific reject and suspect codes * and must be freed with the delete [] operator. */ char* GetUNLVText(); /** * The recognized text is returned as a char* which is coded * as UTF8 and must be freed with the delete [] operator. * page_number is a 0-based page index that will appear in the osd file. */ char* GetOsdText(int page_number); /** Returns the (average) confidence value between 0 and 100. */ int MeanTextConf(); /** * Returns all word confidences (between 0 and 100) in an array, terminated * by -1. The calling function must delete [] after use. * The number of confidences should correspond to the number of space- * delimited words in GetUTF8Text. */ int* AllWordConfidences(); /** * Applies the given word to the adaptive classifier if possible. * The word must be SPACE-DELIMITED UTF-8 - l i k e t h i s , so it can * tell the boundaries of the graphemes. * Assumes that SetImage/SetRectangle have been used to set the image * to the given word. The mode arg should be PSM_SINGLE_WORD or * PSM_CIRCLE_WORD, as that will be used to control layout analysis. * The currently set PageSegMode is preserved. * Returns false if adaption was not possible for some reason. */ bool AdaptToWordStr(PageSegMode mode, const char* wordstr); /** * Free up recognition results and any stored image data, without actually * freeing any recognition data that would be time-consuming to reload. * Afterwards, you must call SetImage or TesseractRect before doing * any Recognize or Get* operation. */ void Clear(); /** * Close down tesseract and free up all memory. End() is equivalent to * destructing and reconstructing your TessBaseAPI. * Once End() has been used, none of the other API functions may be used * other than Init and anything declared above it in the class definition. */ void End(); /** * Clear any library-level memory caches. * There are a variety of expensive-to-load constant data structures (mostly * language dictionaries) that are cached globally -- surviving the Init() * and End() of individual TessBaseAPI's. This function allows the clearing * of these caches. **/ static void ClearPersistentCache(); /** * Check whether a word is valid according to Tesseract's language model * @return 0 if the word is invalid, non-zero if valid. * @warning temporary! This function will be removed from here and placed * in a separate API at some future time. */ int IsValidWord(const char *word); // Returns true if utf8_character is defined in the UniCharset. bool IsValidCharacter(const char *utf8_character); bool GetTextDirection(int* out_offset, float* out_slope); /** Sets Dict::letter_is_okay_ function to point to the given function. */ void SetDictFunc(DictFunc f); /** Sets Dict::probability_in_context_ function to point to the given * function. */ void SetProbabilityInContextFunc(ProbabilityInContextFunc f); /** Sets Wordrec::fill_lattice_ function to point to the given function. */ void SetFillLatticeFunc(FillLatticeFunc f); /** * Estimates the Orientation And Script of the image. * @return true if the image was processed successfully. */ bool DetectOS(OSResults*); /** This method returns the features associated with the input image. */ void GetFeaturesForBlob(TBLOB* blob, INT_FEATURE_STRUCT* int_features, int* num_features, int* feature_outline_index); /** * This method returns the row to which a box of specified dimensions would * belong. If no good match is found, it returns NULL. */ static ROW* FindRowForBox(BLOCK_LIST* blocks, int left, int top, int right, int bottom); /** * Method to run adaptive classifier on a blob. * It returns at max num_max_matches results. */ void RunAdaptiveClassifier(TBLOB* blob, int num_max_matches, int* unichar_ids, float* ratings, int* num_matches_returned); /** This method returns the string form of the specified unichar. */ const char* GetUnichar(int unichar_id); /** Return the pointer to the i-th dawg loaded into tesseract_ object. */ const Dawg *GetDawg(int i) const; /** Return the number of dawgs loaded into tesseract_ object. */ int NumDawgs() const; /** Returns a ROW object created from the input row specification. */ static ROW *MakeTessOCRRow(float baseline, float xheight, float descender, float ascender); /** Returns a TBLOB corresponding to the entire input image. */ static TBLOB *MakeTBLOB(Pix *pix); /** * This method baseline normalizes a TBLOB in-place. The input row is used * for normalization. The denorm is an optional parameter in which the * normalization-antidote is returned. */ static void NormalizeTBLOB(TBLOB *tblob, ROW *row, bool numeric_mode); Tesseract* tesseract() const { return tesseract_; } OcrEngineMode oem() const { return last_oem_requested_; } void InitTruthCallback(TruthCallback *cb) { truth_cb_ = cb; } #ifndef NO_CUBE_BUILD /** Return a pointer to underlying CubeRecoContext object if present. */ CubeRecoContext *GetCubeRecoContext() const; #endif // NO_CUBE_BUILD void set_min_orientation_margin(double margin); /** * Return text orientation of each block as determined by an earlier run * of layout analysis. */ void GetBlockTextOrientations(int** block_orientation, bool** vertical_writing); /** Find lines from the image making the BLOCK_LIST. */ BLOCK_LIST* FindLinesCreateBlockList(); /** * Delete a block list. * This is to keep BLOCK_LIST pointer opaque * and let go of including the other headers. */ static void DeleteBlockList(BLOCK_LIST* block_list); /* @} */ protected: /** Common code for setting the image. Returns true if Init has been called. */ TESS_LOCAL bool InternalSetImage(); /** * Run the thresholder to make the thresholded image. If pix is not NULL, * the source is thresholded to pix instead of the internal IMAGE. */ TESS_LOCAL virtual void Threshold(Pix** pix); /** * Find lines from the image making the BLOCK_LIST. * @return 0 on success. */ TESS_LOCAL int FindLines(); /** Delete the pageres and block list ready for a new page. */ void ClearResults(); /** * Return an LTR Result Iterator -- used only for training, as we really want * to ignore all BiDi smarts at that point. * delete once you're done with it. */ TESS_LOCAL LTRResultIterator* GetLTRIterator(); /** * Return the length of the output text string, as UTF8, assuming * one newline per line and one per block, with a terminator, * and assuming a single character reject marker for each rejected character. * Also return the number of recognized blobs in blob_count. */ TESS_LOCAL int TextLength(int* blob_count); /** @defgroup ocropusAddOns ocropus add-ons */ /* @{ */ /** * Adapt to recognize the current image as the given character. * The image must be preloaded and be just an image of a single character. */ TESS_LOCAL void AdaptToCharacter(const char *unichar_repr, int length, float baseline, float xheight, float descender, float ascender); /** Recognize text doing one pass only, using settings for a given pass. */ TESS_LOCAL PAGE_RES* RecognitionPass1(BLOCK_LIST* block_list); TESS_LOCAL PAGE_RES* RecognitionPass2(BLOCK_LIST* block_list, PAGE_RES* pass1_result); //// paragraphs.cpp //////////////////////////////////////////////////// TESS_LOCAL void DetectParagraphs(bool after_text_recognition); /** * Extract the OCR results, costs (penalty points for uncertainty), * and the bounding boxes of the characters. */ TESS_LOCAL static int TesseractExtractResult(char** text, int** lengths, float** costs, int** x0, int** y0, int** x1, int** y1, PAGE_RES* page_res); TESS_LOCAL const PAGE_RES* GetPageRes() const { return page_res_; }; /* @} */ protected: Tesseract* tesseract_; ///< The underlying data object. Tesseract* osd_tesseract_; ///< For orientation & script detection. EquationDetect* equ_detect_; ///* paragraph_models_; BLOCK_LIST* block_list_; ///< The page layout. PAGE_RES* page_res_; ///< The page-level data. STRING* input_file_; ///< Name used by training code. Pix* input_image_; ///< Image used for searchable PDF STRING* output_file_; ///< Name used by debug code. STRING* datapath_; ///< Current location of tessdata. STRING* language_; ///< Last initialized language. OcrEngineMode last_oem_requested_; ///< Last ocr language mode requested. bool recognition_done_; ///< page_res_ contains recognition data. TruthCallback *truth_cb_; /// fxn for setting truth_* in WERD_RES /** * @defgroup ThresholderParams Thresholder Parameters * Parameters saved from the Thresholder. Needed to rebuild coordinates. */ /* @{ */ int rect_left_; int rect_top_; int rect_width_; int rect_height_; int image_width_; int image_height_; /* @} */ private: // A list of image filenames gets special consideration bool ProcessPagesFileList(FILE *fp, STRING *buf, const char* retry_config, int timeout_millisec, TessResultRenderer* renderer, int tessedit_page_number); // TIFF supports multipage so gets special consideration bool ProcessPagesMultipageTiff(const unsigned char *data, size_t size, const char* filename, const char* retry_config, int timeout_millisec, TessResultRenderer* renderer, int tessedit_page_number); }; // class TessBaseAPI. /** Escape a char string - remove &<>"' with HTML codes. */ STRING HOcrEscape(const char* text); } // namespace tesseract. #endif // TESSERACT_API_BASEAPI_H__ tesseract-3.04.01/api/capi.cpp000066400000000000000000000607031266071204500160560ustar00rootroot00000000000000#ifndef TESS_CAPI_INCLUDE_BASEAPI # define TESS_CAPI_INCLUDE_BASEAPI #endif #include "capi.h" #include "genericvector.h" #include "strngs.h" TESS_API const char* TESS_CALL TessVersion() { return TessBaseAPI::Version(); } TESS_API void TESS_CALL TessDeleteText(char* text) { delete [] text; } TESS_API void TESS_CALL TessDeleteTextArray(char** arr) { for (char** pos = arr; *pos != NULL; ++pos) delete [] *pos; delete [] arr; } TESS_API void TESS_CALL TessDeleteIntArray(int* arr) { delete [] arr; } TESS_API void TESS_CALL TessDeleteBlockList(BLOCK_LIST* block_list) { TessBaseAPI::DeleteBlockList(block_list); } TESS_API TessResultRenderer* TESS_CALL TessTextRendererCreate(const char* outputbase) { return new TessTextRenderer(outputbase); } TESS_API TessResultRenderer* TESS_CALL TessHOcrRendererCreate(const char* outputbase) { return new TessHOcrRenderer(outputbase); } TESS_API TessResultRenderer* TESS_CALL TessHOcrRendererCreate2(const char* outputbase, BOOL font_info) { return new TessHOcrRenderer(outputbase, font_info); } TESS_API TessResultRenderer* TESS_CALL TessPDFRendererCreate(const char* outputbase, const char* datadir) { return new TessPDFRenderer(outputbase, datadir); } TESS_API TessResultRenderer* TESS_CALL TessUnlvRendererCreate(const char* outputbase) { return new TessUnlvRenderer(outputbase); } TESS_API TessResultRenderer* TESS_CALL TessBoxTextRendererCreate(const char* outputbase) { return new TessBoxTextRenderer(outputbase); } TESS_API void TESS_CALL TessDeleteResultRenderer(TessResultRenderer* renderer) { delete renderer; } TESS_API void TESS_CALL TessResultRendererInsert(TessResultRenderer* renderer, TessResultRenderer* next) { renderer->insert(next); } TESS_API TessResultRenderer* TESS_CALL TessResultRendererNext(TessResultRenderer* renderer) { return renderer->next(); } TESS_API BOOL TESS_CALL TessResultRendererBeginDocument(TessResultRenderer* renderer, const char* title) { return renderer->BeginDocument(title); } TESS_API BOOL TESS_CALL TessResultRendererAddImage(TessResultRenderer* renderer, TessBaseAPI* api) { return renderer->AddImage(api); } TESS_API BOOL TESS_CALL TessResultRendererEndDocument(TessResultRenderer* renderer) { return renderer->EndDocument(); } TESS_API const char* TESS_CALL TessResultRendererExtention(TessResultRenderer* renderer) { return renderer->file_extension(); } TESS_API const char* TESS_CALL TessResultRendererTitle(TessResultRenderer* renderer) { return renderer->title(); } TESS_API int TESS_CALL TessResultRendererImageNum(TessResultRenderer* renderer) { return renderer->imagenum(); } TESS_API TessBaseAPI* TESS_CALL TessBaseAPICreate() { return new TessBaseAPI; } TESS_API void TESS_CALL TessBaseAPIDelete(TessBaseAPI* handle) { delete handle; } TESS_API size_t TESS_CALL TessBaseAPIGetOpenCLDevice(TessBaseAPI* handle, void **device) { return handle->getOpenCLDevice(device); } TESS_API void TESS_CALL TessBaseAPISetInputName(TessBaseAPI* handle, const char* name) { handle->SetInputName(name); } TESS_API const char* TESS_CALL TessBaseAPIGetInputName(TessBaseAPI* handle) { return handle->GetInputName(); } TESS_API void TESS_CALL TessBaseAPISetInputImage(TessBaseAPI* handle, Pix* pix) { handle->SetInputImage(pix); } TESS_API Pix* TESS_CALL TessBaseAPIGetInputImage(TessBaseAPI* handle) { return handle->GetInputImage(); } TESS_API int TESS_CALL TessBaseAPIGetSourceYResolution(TessBaseAPI* handle) { return handle->GetSourceYResolution(); } TESS_API const char* TESS_CALL TessBaseAPIGetDatapath(TessBaseAPI* handle) { return handle->GetDatapath(); } TESS_API void TESS_CALL TessBaseAPISetOutputName(TessBaseAPI* handle, const char* name) { handle->SetOutputName(name); } TESS_API BOOL TESS_CALL TessBaseAPISetVariable(TessBaseAPI* handle, const char* name, const char* value) { return handle->SetVariable(name, value) ? TRUE : FALSE; } TESS_API BOOL TESS_CALL TessBaseAPISetDebugVariable(TessBaseAPI* handle, const char* name, const char* value) { return handle->SetVariable(name, value) ? TRUE : FALSE; } TESS_API BOOL TESS_CALL TessBaseAPIGetIntVariable(const TessBaseAPI* handle, const char* name, int* value) { return handle->GetIntVariable(name, value) ? TRUE : FALSE; } TESS_API BOOL TESS_CALL TessBaseAPIGetBoolVariable(const TessBaseAPI* handle, const char* name, BOOL* value) { bool boolValue; if (handle->GetBoolVariable(name, &boolValue)) { *value = boolValue ? TRUE : FALSE; return TRUE; } else { return FALSE; } } TESS_API BOOL TESS_CALL TessBaseAPIGetDoubleVariable(const TessBaseAPI* handle, const char* name, double* value) { return handle->GetDoubleVariable(name, value) ? TRUE : FALSE; } TESS_API const char* TESS_CALL TessBaseAPIGetStringVariable(const TessBaseAPI* handle, const char* name) { return handle->GetStringVariable(name); } TESS_API void TESS_CALL TessBaseAPIPrintVariables(const TessBaseAPI* handle, FILE* fp) { handle->PrintVariables(fp); } TESS_API BOOL TESS_CALL TessBaseAPIPrintVariablesToFile(const TessBaseAPI* handle, const char* filename) { FILE* fp = fopen(filename, "w"); if (fp != NULL) { handle->PrintVariables(fp); fclose(fp); return TRUE; } return FALSE; } TESS_API BOOL TESS_CALL TessBaseAPIGetVariableAsString(TessBaseAPI* handle, const char* name, STRING* val) { return handle->GetVariableAsString(name, val) ? TRUE : FALSE; } TESS_API int TESS_CALL TessBaseAPIInit4(TessBaseAPI* handle, const char* datapath, const char* language, TessOcrEngineMode mode, char** configs, int configs_size, char** vars_vec, char** vars_values, size_t vars_vec_size, BOOL set_only_non_debug_params) { GenericVector varNames; GenericVector varValues; if (vars_vec != NULL && vars_values != NULL) { for (size_t i = 0; i < vars_vec_size; i++) { varNames.push_back(STRING(vars_vec[i])); varValues.push_back(STRING(vars_values[i])); } } return handle->Init(datapath, language, mode, configs, configs_size, &varNames, &varValues, set_only_non_debug_params); } TESS_API int TESS_CALL TessBaseAPIInit1(TessBaseAPI* handle, const char* datapath, const char* language, TessOcrEngineMode oem, char** configs, int configs_size) { return handle->Init(datapath, language, oem, configs, configs_size, NULL, NULL, false); } TESS_API int TESS_CALL TessBaseAPIInit2(TessBaseAPI* handle, const char* datapath, const char* language, TessOcrEngineMode oem) { return handle->Init(datapath, language, oem); } TESS_API int TESS_CALL TessBaseAPIInit3(TessBaseAPI* handle, const char* datapath, const char* language) { return handle->Init(datapath, language); } TESS_API const char* TESS_CALL TessBaseAPIGetInitLanguagesAsString(const TessBaseAPI* handle) { return handle->GetInitLanguagesAsString(); } TESS_API char** TESS_CALL TessBaseAPIGetLoadedLanguagesAsVector(const TessBaseAPI* handle) { GenericVector languages; handle->GetLoadedLanguagesAsVector(&languages); char** arr = new char*[languages.size() + 1]; for (int index = 0; index < languages.size(); ++index) arr[index] = languages[index].strdup(); arr[languages.size()] = NULL; return arr; } TESS_API char** TESS_CALL TessBaseAPIGetAvailableLanguagesAsVector(const TessBaseAPI* handle) { GenericVector languages; handle->GetAvailableLanguagesAsVector(&languages); char** arr = new char*[languages.size() + 1]; for (int index = 0; index < languages.size(); ++index) arr[index] = languages[index].strdup(); arr[languages.size()] = NULL; return arr; } TESS_API int TESS_CALL TessBaseAPIInitLangMod(TessBaseAPI* handle, const char* datapath, const char* language) { return handle->InitLangMod(datapath, language); } TESS_API void TESS_CALL TessBaseAPIInitForAnalysePage(TessBaseAPI* handle) { handle->InitForAnalysePage(); } TESS_API void TESS_CALL TessBaseAPIReadConfigFile(TessBaseAPI* handle, const char* filename) { handle->ReadConfigFile(filename); } TESS_API void TESS_CALL TessBaseAPIReadDebugConfigFile(TessBaseAPI* handle, const char* filename) { handle->ReadDebugConfigFile(filename); } TESS_API void TESS_CALL TessBaseAPISetPageSegMode(TessBaseAPI* handle, TessPageSegMode mode) { handle->SetPageSegMode(mode); } TESS_API TessPageSegMode TESS_CALL TessBaseAPIGetPageSegMode(const TessBaseAPI* handle) { return handle->GetPageSegMode(); } TESS_API char* TESS_CALL TessBaseAPIRect(TessBaseAPI* handle, const unsigned char* imagedata, int bytes_per_pixel, int bytes_per_line, int left, int top, int width, int height) { return handle->TesseractRect(imagedata, bytes_per_pixel, bytes_per_line, left, top, width, height); } TESS_API void TESS_CALL TessBaseAPIClearAdaptiveClassifier(TessBaseAPI* handle) { handle->ClearAdaptiveClassifier(); } TESS_API void TESS_CALL TessBaseAPISetImage(TessBaseAPI* handle, const unsigned char* imagedata, int width, int height, int bytes_per_pixel, int bytes_per_line) { handle->SetImage(imagedata, width, height, bytes_per_pixel, bytes_per_line); } TESS_API void TESS_CALL TessBaseAPISetImage2(TessBaseAPI* handle, struct Pix* pix) { return handle->SetImage(pix); } TESS_API void TESS_CALL TessBaseAPISetSourceResolution(TessBaseAPI* handle, int ppi) { handle->SetSourceResolution(ppi); } TESS_API void TESS_CALL TessBaseAPISetRectangle(TessBaseAPI* handle, int left, int top, int width, int height) { handle->SetRectangle(left, top, width, height); } TESS_API void TESS_CALL TessBaseAPISetThresholder(TessBaseAPI* handle, TessImageThresholder* thresholder) { handle->SetThresholder(thresholder); } TESS_API struct Pix* TESS_CALL TessBaseAPIGetThresholdedImage(TessBaseAPI* handle) { return handle->GetThresholdedImage(); } TESS_API struct Boxa* TESS_CALL TessBaseAPIGetRegions(TessBaseAPI* handle, struct Pixa** pixa) { return handle->GetRegions(pixa); } TESS_API struct Boxa* TESS_CALL TessBaseAPIGetTextlines(TessBaseAPI* handle, struct Pixa** pixa, int** blockids) { return handle->GetTextlines(pixa, blockids); } TESS_API struct Boxa* TESS_CALL TessBaseAPIGetTextlines1(TessBaseAPI* handle, const BOOL raw_image, const int raw_padding, struct Pixa** pixa, int** blockids, int** paraids) { return handle->GetTextlines(raw_image, raw_padding, pixa, blockids, paraids); } TESS_API struct Boxa* TESS_CALL TessBaseAPIGetStrips(TessBaseAPI* handle, struct Pixa** pixa, int** blockids) { return handle->GetStrips(pixa, blockids); } TESS_API struct Boxa* TESS_CALL TessBaseAPIGetWords(TessBaseAPI* handle, struct Pixa** pixa) { return handle->GetWords(pixa); } TESS_API struct Boxa* TESS_CALL TessBaseAPIGetConnectedComponents(TessBaseAPI* handle, struct Pixa** cc) { return handle->GetConnectedComponents(cc); } TESS_API struct Boxa* TESS_CALL TessBaseAPIGetComponentImages(TessBaseAPI* handle, TessPageIteratorLevel level, BOOL text_only, struct Pixa** pixa, int** blockids) { return handle->GetComponentImages(level, text_only != FALSE, pixa, blockids); } TESS_API struct Boxa* TESS_CALL TessBaseAPIGetComponentImages1( TessBaseAPI* handle, const TessPageIteratorLevel level, const BOOL text_only, const BOOL raw_image, const int raw_padding, struct Pixa** pixa, int** blockids, int** paraids) { return handle->GetComponentImages(level, text_only != FALSE, raw_image, raw_padding, pixa, blockids, paraids); } TESS_API int TESS_CALL TessBaseAPIGetThresholdedImageScaleFactor(const TessBaseAPI* handle) { return handle->GetThresholdedImageScaleFactor(); } TESS_API void TESS_CALL TessBaseAPIDumpPGM(TessBaseAPI* handle, const char* filename) { handle->DumpPGM(filename); } TESS_API TessPageIterator* TESS_CALL TessBaseAPIAnalyseLayout(TessBaseAPI* handle) { return handle->AnalyseLayout(); } TESS_API int TESS_CALL TessBaseAPIRecognize(TessBaseAPI* handle, ETEXT_DESC* monitor) { return handle->Recognize(monitor); } TESS_API int TESS_CALL TessBaseAPIRecognizeForChopTest(TessBaseAPI* handle, ETEXT_DESC* monitor) { return handle->RecognizeForChopTest(monitor); } TESS_API BOOL TESS_CALL TessBaseAPIProcessPages(TessBaseAPI* handle, const char* filename, const char* retry_config, int timeout_millisec, TessResultRenderer* renderer) { if (handle->ProcessPages(filename, retry_config, timeout_millisec, renderer)) return TRUE; else return FALSE; } TESS_API BOOL TESS_CALL TessBaseAPIProcessPage(TessBaseAPI* handle, struct Pix* pix, int page_index, const char* filename, const char* retry_config, int timeout_millisec, TessResultRenderer* renderer) { if (handle->ProcessPage(pix, page_index, filename, retry_config, timeout_millisec, renderer)) return TRUE; else return FALSE; } TESS_API TessResultIterator* TESS_CALL TessBaseAPIGetIterator(TessBaseAPI* handle) { return handle->GetIterator(); } TESS_API TessMutableIterator* TESS_CALL TessBaseAPIGetMutableIterator(TessBaseAPI* handle) { return handle->GetMutableIterator(); } TESS_API char* TESS_CALL TessBaseAPIGetUTF8Text(TessBaseAPI* handle) { return handle->GetUTF8Text(); } TESS_API char* TESS_CALL TessBaseAPIGetHOCRText(TessBaseAPI* handle, int page_number) { return handle->GetHOCRText(page_number); } TESS_API char* TESS_CALL TessBaseAPIGetBoxText(TessBaseAPI* handle, int page_number) { return handle->GetBoxText(page_number); } TESS_API char* TESS_CALL TessBaseAPIGetUNLVText(TessBaseAPI* handle) { return handle->GetUNLVText(); } TESS_API int TESS_CALL TessBaseAPIMeanTextConf(TessBaseAPI* handle) { return handle->MeanTextConf(); } TESS_API int* TESS_CALL TessBaseAPIAllWordConfidences(TessBaseAPI* handle) { return handle->AllWordConfidences(); } TESS_API BOOL TESS_CALL TessBaseAPIAdaptToWordStr(TessBaseAPI* handle, TessPageSegMode mode, const char* wordstr) { return handle->AdaptToWordStr(mode, wordstr) ? TRUE : FALSE; } TESS_API void TESS_CALL TessBaseAPIClear(TessBaseAPI* handle) { handle->Clear(); } TESS_API void TESS_CALL TessBaseAPIEnd(TessBaseAPI* handle) { handle->End(); } TESS_API int TESS_CALL TessBaseAPIIsValidWord(TessBaseAPI* handle, const char* word) { return handle->IsValidWord(word); } TESS_API BOOL TESS_CALL TessBaseAPIGetTextDirection(TessBaseAPI* handle, int* out_offset, float* out_slope) { return handle->GetTextDirection(out_offset, out_slope) ? TRUE : FALSE; } TESS_API void TESS_CALL TessBaseAPISetDictFunc(TessBaseAPI* handle, TessDictFunc f) { handle->SetDictFunc(f); } TESS_API void TESS_CALL TessBaseAPIClearPersistentCache(TessBaseAPI* handle) { handle->ClearPersistentCache(); } TESS_API void TESS_CALL TessBaseAPISetProbabilityInContextFunc(TessBaseAPI* handle, TessProbabilityInContextFunc f) { handle->SetProbabilityInContextFunc(f); } TESS_API BOOL TESS_CALL TessBaseAPIDetectOS(TessBaseAPI* handle, OSResults* results) { return handle->DetectOS(results) ? TRUE : FALSE; } TESS_API void TESS_CALL TessBaseAPIGetFeaturesForBlob(TessBaseAPI* handle, TBLOB* blob, INT_FEATURE_STRUCT* int_features, int* num_features, int* 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) { return TessBaseAPI::FindRowForBox(blocks, left, top, right, bottom); } 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, num_max_matches, unichar_ids, ratings, num_matches_returned); } TESS_API const char* TESS_CALL TessBaseAPIGetUnichar(TessBaseAPI* handle, int unichar_id) { return handle->GetUnichar(unichar_id); } TESS_API const TessDawg* TESS_CALL TessBaseAPIGetDawg(const TessBaseAPI* handle, int i) { return handle->GetDawg(i); } TESS_API int TESS_CALL TessBaseAPINumDawgs(const TessBaseAPI* handle) { return handle->NumDawgs(); } TESS_API ROW* TESS_CALL TessMakeTessOCRRow(float baseline, float xheight, float descender, float ascender) { return TessBaseAPI::MakeTessOCRRow(baseline, xheight, descender, ascender); } 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) { TessBaseAPI::NormalizeTBLOB(tblob, row, numeric_mode != FALSE); } TESS_API TessOcrEngineMode TESS_CALL TessBaseAPIOem(const TessBaseAPI* handle) { return handle->oem(); } TESS_API void TESS_CALL TessBaseAPIInitTruthCallback(TessBaseAPI* handle, TessTruthCallback* cb) { handle->InitTruthCallback(cb); } #ifndef NO_CUBE_BUILD TESS_API TessCubeRecoContext* TESS_CALL TessBaseAPIGetCubeRecoContext(const TessBaseAPI* handle) { return handle->GetCubeRecoContext(); } #endif // NO_CUBE_BUILD TESS_API void TESS_CALL TessBaseAPISetMinOrientationMargin(TessBaseAPI* handle, double margin) { handle->set_min_orientation_margin(margin); } TESS_API void TESS_CALL TessBaseGetBlockTextOrientations(TessBaseAPI* handle, int** block_orientation, bool** vertical_writing) { handle->GetBlockTextOrientations(block_orientation, vertical_writing); } TESS_API BLOCK_LIST* TESS_CALL TessBaseAPIFindLinesCreateBlockList(TessBaseAPI* handle) { return handle->FindLinesCreateBlockList(); } TESS_API void TESS_CALL TessPageIteratorDelete(TessPageIterator* handle) { delete handle; } TESS_API TessPageIterator* TESS_CALL TessPageIteratorCopy(const TessPageIterator* handle) { return new TessPageIterator(*handle); } TESS_API void TESS_CALL TessPageIteratorBegin(TessPageIterator* handle) { handle->Begin(); } TESS_API BOOL TESS_CALL TessPageIteratorNext(TessPageIterator* handle, TessPageIteratorLevel level) { return handle->Next(level) ? TRUE : FALSE; } TESS_API BOOL TESS_CALL TessPageIteratorIsAtBeginningOf(const TessPageIterator* handle, TessPageIteratorLevel level) { return handle->IsAtBeginningOf(level) ? TRUE : FALSE; } TESS_API BOOL TESS_CALL TessPageIteratorIsAtFinalElement(const TessPageIterator* handle, TessPageIteratorLevel level, TessPageIteratorLevel element) { return handle->IsAtFinalElement(level, element) ? TRUE : FALSE; } TESS_API BOOL TESS_CALL TessPageIteratorBoundingBox(const TessPageIterator* handle, TessPageIteratorLevel level, int* left, int* top, int* right, int* bottom) { return handle->BoundingBox(level, left, top, right, bottom) ? TRUE : FALSE; } TESS_API TessPolyBlockType TESS_CALL TessPageIteratorBlockType(const TessPageIterator* handle) { return handle->BlockType(); } TESS_API struct Pix* TESS_CALL TessPageIteratorGetBinaryImage(const TessPageIterator* handle, TessPageIteratorLevel level) { return handle->GetBinaryImage(level); } TESS_API struct Pix* TESS_CALL TessPageIteratorGetImage(const TessPageIterator* handle, TessPageIteratorLevel level, int padding, struct Pix* original_image, int* left, int* top) { return handle->GetImage(level, padding, original_image, left, top); } TESS_API BOOL TESS_CALL TessPageIteratorBaseline(const TessPageIterator* handle, TessPageIteratorLevel level, int* x1, int* y1, int* x2, int* y2) { return handle->Baseline(level, x1, y1, x2, y2) ? TRUE : FALSE; } TESS_API void TESS_CALL TessPageIteratorOrientation(TessPageIterator* handle, TessOrientation* orientation, TessWritingDirection* writing_direction, TessTextlineOrder* textline_order, float* deskew_angle) { handle->Orientation(orientation, writing_direction, textline_order, deskew_angle); } TESS_API void TESS_CALL TessPageIteratorParagraphInfo(TessPageIterator* handle, TessParagraphJustification* justification, BOOL *is_list_item, BOOL *is_crown, int *first_line_indent) { bool bool_is_list_item, bool_is_crown; handle->ParagraphInfo(justification, &bool_is_list_item, &bool_is_crown, first_line_indent); if (is_list_item) *is_list_item = bool_is_list_item ? TRUE : FALSE; if (is_crown) *is_crown = bool_is_crown ? TRUE : FALSE; } TESS_API void TESS_CALL TessResultIteratorDelete(TessResultIterator* handle) { delete handle; } TESS_API TessResultIterator* TESS_CALL TessResultIteratorCopy(const TessResultIterator* handle) { return new TessResultIterator(*handle); } TESS_API TessPageIterator* TESS_CALL TessResultIteratorGetPageIterator(TessResultIterator* handle) { return handle; } TESS_API const TessPageIterator* TESS_CALL TessResultIteratorGetPageIteratorConst(const TessResultIterator* handle) { return handle; } TESS_API TessChoiceIterator* TESS_CALL TessResultIteratorGetChoiceIterator(const TessResultIterator* handle) { return new TessChoiceIterator(*handle); } TESS_API BOOL TESS_CALL TessResultIteratorNext(TessResultIterator* handle, TessPageIteratorLevel level) { return handle->Next(level); } TESS_API char* TESS_CALL TessResultIteratorGetUTF8Text(const TessResultIterator* handle, TessPageIteratorLevel level) { return handle->GetUTF8Text(level); } TESS_API float TESS_CALL TessResultIteratorConfidence(const TessResultIterator* handle, TessPageIteratorLevel level) { return handle->Confidence(level); } TESS_API const char* TESS_CALL TessResultIteratorWordRecognitionLanguage(const TessResultIterator* handle) { return handle->WordRecognitionLanguage(); } TESS_API const char* TESS_CALL TessResultIteratorWordFontAttributes(const TessResultIterator* handle, BOOL* is_bold, BOOL* is_italic, BOOL* is_underlined, BOOL* is_monospace, BOOL* is_serif, BOOL* is_smallcaps, int* pointsize, int* font_id) { bool bool_is_bold, bool_is_italic, bool_is_underlined, bool_is_monospace, bool_is_serif, bool_is_smallcaps; const char* ret = handle->WordFontAttributes(&bool_is_bold, &bool_is_italic, &bool_is_underlined, &bool_is_monospace, &bool_is_serif, &bool_is_smallcaps, pointsize, font_id); if (is_bold) *is_bold = bool_is_bold ? TRUE : FALSE; if (is_italic) *is_italic = bool_is_italic ? TRUE : FALSE; if (is_underlined) *is_underlined = bool_is_underlined ? TRUE : FALSE; if (is_monospace) *is_monospace = bool_is_monospace ? TRUE : FALSE; if (is_serif) *is_serif = bool_is_serif ? TRUE : FALSE; if (is_smallcaps) *is_smallcaps = bool_is_smallcaps ? TRUE : FALSE; return ret; } TESS_API BOOL TESS_CALL TessResultIteratorWordIsFromDictionary(const TessResultIterator* handle) { return handle->WordIsFromDictionary() ? TRUE : FALSE; } TESS_API BOOL TESS_CALL TessResultIteratorWordIsNumeric(const TessResultIterator* handle) { return handle->WordIsNumeric() ? TRUE : FALSE; } TESS_API BOOL TESS_CALL TessResultIteratorSymbolIsSuperscript(const TessResultIterator* handle) { return handle->SymbolIsSuperscript() ? TRUE : FALSE; } TESS_API BOOL TESS_CALL TessResultIteratorSymbolIsSubscript(const TessResultIterator* handle) { return handle->SymbolIsSubscript() ? TRUE : FALSE; } TESS_API BOOL TESS_CALL TessResultIteratorSymbolIsDropcap(const TessResultIterator* handle) { return handle->SymbolIsDropcap() ? TRUE : FALSE; } TESS_API void TESS_CALL TessChoiceIteratorDelete(TessChoiceIterator* handle) { delete handle; } TESS_API BOOL TESS_CALL TessChoiceIteratorNext(TessChoiceIterator* handle) { return handle->Next(); } TESS_API const char* TESS_CALL TessChoiceIteratorGetUTF8Text(const TessChoiceIterator* handle) { return handle->GetUTF8Text(); } TESS_API float TESS_CALL TessChoiceIteratorConfidence(const TessChoiceIterator* handle) { return handle->Confidence(); } tesseract-3.04.01/api/capi.h000066400000000000000000000522511266071204500155220ustar00rootroot00000000000000#ifndef TESSERACT_API_CAPI_H__ #define TESSERACT_API_CAPI_H__ #ifdef TESS_CAPI_INCLUDE_BASEAPI # include "baseapi.h" # include "pageiterator.h" # include "resultiterator.h" # include "renderer.h" #else # include "platform.h" # include #endif #ifdef __cplusplus extern "C" { #endif #ifndef TESS_CALL # if defined(WIN32) # define TESS_CALL __cdecl # else # define TESS_CALL # endif #endif #ifndef BOOL # define BOOL int # define TRUE 1 # define FALSE 0 #endif #ifdef TESS_CAPI_INCLUDE_BASEAPI typedef tesseract::TessResultRenderer TessResultRenderer; typedef tesseract::TessTextRenderer TessTextRenderer; typedef tesseract::TessHOcrRenderer TessHOcrRenderer; typedef tesseract::TessPDFRenderer TessPDFRenderer; typedef tesseract::TessUnlvRenderer TessUnlvRenderer; typedef tesseract::TessBoxTextRenderer TessBoxTextRenderer; typedef tesseract::TessBaseAPI TessBaseAPI; typedef tesseract::PageIterator TessPageIterator; typedef tesseract::ResultIterator TessResultIterator; typedef tesseract::MutableIterator TessMutableIterator; typedef tesseract::ChoiceIterator TessChoiceIterator; typedef tesseract::OcrEngineMode TessOcrEngineMode; typedef tesseract::PageSegMode TessPageSegMode; typedef tesseract::ImageThresholder TessImageThresholder; typedef tesseract::PageIteratorLevel TessPageIteratorLevel; typedef tesseract::DictFunc TessDictFunc; typedef tesseract::ProbabilityInContextFunc TessProbabilityInContextFunc; // typedef tesseract::ParamsModelClassifyFunc TessParamsModelClassifyFunc; typedef tesseract::FillLatticeFunc TessFillLatticeFunc; typedef tesseract::Dawg TessDawg; typedef tesseract::TruthCallback TessTruthCallback; #ifndef NO_CUBE_BUILD typedef tesseract::CubeRecoContext TessCubeRecoContext; #endif // NO_CUBE_BUILD typedef tesseract::Orientation TessOrientation; typedef tesseract::ParagraphJustification TessParagraphJustification; typedef tesseract::WritingDirection TessWritingDirection; typedef tesseract::TextlineOrder TessTextlineOrder; typedef PolyBlockType TessPolyBlockType; #else typedef struct TessResultRenderer TessResultRenderer; typedef struct TessTextRenderer TessTextRenderer; typedef struct TessHOcrRenderer TessHOcrRenderer; typedef struct TessPDFRenderer TessPDFRenderer; typedef struct TessUnlvRenderer TessUnlvRenderer; typedef struct TessBoxTextRenderer TessBoxTextRenderer; typedef struct TessBaseAPI TessBaseAPI; typedef struct TessPageIterator TessPageIterator; typedef struct TessResultIterator TessResultIterator; typedef struct TessMutableIterator TessMutableIterator; typedef struct TessChoiceIterator TessChoiceIterator; typedef enum TessOcrEngineMode { OEM_TESSERACT_ONLY, OEM_CUBE_ONLY, OEM_TESSERACT_CUBE_COMBINED, OEM_DEFAULT } TessOcrEngineMode; typedef enum TessPageSegMode { PSM_OSD_ONLY, PSM_AUTO_OSD, PSM_AUTO_ONLY, PSM_AUTO, PSM_SINGLE_COLUMN, PSM_SINGLE_BLOCK_VERT_TEXT, PSM_SINGLE_BLOCK, PSM_SINGLE_LINE, PSM_SINGLE_WORD, PSM_CIRCLE_WORD, PSM_SINGLE_CHAR, PSM_SPARSE_TEXT, PSM_SPARSE_TEXT_OSD, PSM_COUNT } TessPageSegMode; typedef enum TessPageIteratorLevel { RIL_BLOCK, RIL_PARA, RIL_TEXTLINE, RIL_WORD, RIL_SYMBOL} TessPageIteratorLevel; typedef enum TessPolyBlockType { PT_UNKNOWN, PT_FLOWING_TEXT, PT_HEADING_TEXT, PT_PULLOUT_TEXT, PT_EQUATION, PT_INLINE_EQUATION, PT_TABLE, PT_VERTICAL_TEXT, PT_CAPTION_TEXT, PT_FLOWING_IMAGE, PT_HEADING_IMAGE, PT_PULLOUT_IMAGE, PT_HORZ_LINE, PT_VERT_LINE, PT_NOISE, PT_COUNT } TessPolyBlockType; typedef enum TessOrientation { ORIENTATION_PAGE_UP, ORIENTATION_PAGE_RIGHT, ORIENTATION_PAGE_DOWN, ORIENTATION_PAGE_LEFT } TessOrientation; typedef enum TessParagraphJustification { JUSTIFICATION_UNKNOWN, JUSTIFICATION_LEFT, JUSTIFICATION_CENTER, JUSTIFICATION_RIGHT } TessParagraphJustification; typedef enum TessWritingDirection { WRITING_DIRECTION_LEFT_TO_RIGHT, WRITING_DIRECTION_RIGHT_TO_LEFT, WRITING_DIRECTION_TOP_TO_BOTTOM } TessWritingDirection; typedef enum TessTextlineOrder { TEXTLINE_ORDER_LEFT_TO_RIGHT, TEXTLINE_ORDER_RIGHT_TO_LEFT, TEXTLINE_ORDER_TOP_TO_BOTTOM } TessTextlineOrder; typedef struct ETEXT_DESC ETEXT_DESC; #endif struct Pix; struct Boxa; struct Pixa; /* General free functions */ TESS_API const char* TESS_CALL TessVersion(); TESS_API void TESS_CALL TessDeleteText(char* text); TESS_API void TESS_CALL TessDeleteTextArray(char** arr); TESS_API void TESS_CALL TessDeleteIntArray(int* arr); #ifdef TESS_CAPI_INCLUDE_BASEAPI TESS_API void TESS_CALL TessDeleteBlockList(BLOCK_LIST* block_list); #endif /* Renderer API */ TESS_API TessResultRenderer* TESS_CALL TessTextRendererCreate(const char* outputbase); TESS_API TessResultRenderer* TESS_CALL TessHOcrRendererCreate(const char* outputbase); TESS_API TessResultRenderer* TESS_CALL TessHOcrRendererCreate2(const char* outputbase, BOOL font_info); TESS_API TessResultRenderer* TESS_CALL TessPDFRendererCreate(const char* outputbase, const char* datadir); TESS_API TessResultRenderer* TESS_CALL TessUnlvRendererCreate(const char* outputbase); TESS_API TessResultRenderer* TESS_CALL TessBoxTextRendererCreate(const char* outputbase); TESS_API void TESS_CALL TessDeleteResultRenderer(TessResultRenderer* renderer); TESS_API void TESS_CALL TessResultRendererInsert(TessResultRenderer* renderer, TessResultRenderer* next); TESS_API TessResultRenderer* TESS_CALL TessResultRendererNext(TessResultRenderer* renderer); TESS_API BOOL TESS_CALL TessResultRendererBeginDocument(TessResultRenderer* renderer, const char* title); TESS_API BOOL TESS_CALL TessResultRendererAddImage(TessResultRenderer* renderer, TessBaseAPI* api); TESS_API BOOL TESS_CALL TessResultRendererEndDocument(TessResultRenderer* renderer); TESS_API const char* TESS_CALL TessResultRendererExtention(TessResultRenderer* renderer); TESS_API const char* TESS_CALL TessResultRendererTitle(TessResultRenderer* renderer); TESS_API int TESS_CALL TessResultRendererImageNum(TessResultRenderer* renderer); /* Base API */ TESS_API TessBaseAPI* TESS_CALL TessBaseAPICreate(); TESS_API void TESS_CALL TessBaseAPIDelete(TessBaseAPI* handle); TESS_API size_t TESS_CALL TessBaseAPIGetOpenCLDevice(TessBaseAPI* handle, void **device); TESS_API void TESS_CALL TessBaseAPISetInputName( TessBaseAPI* handle, const char* name); TESS_API const char* TESS_CALL TessBaseAPIGetInputName(TessBaseAPI* handle); TESS_API void TESS_CALL TessBaseAPISetInputImage(TessBaseAPI* handle, struct Pix* pix); TESS_API struct Pix* TESS_CALL TessBaseAPIGetInputImage(TessBaseAPI* handle); TESS_API int TESS_CALL TessBaseAPIGetSourceYResolution(TessBaseAPI* handle); TESS_API const char* TESS_CALL TessBaseAPIGetDatapath(TessBaseAPI* handle); TESS_API void TESS_CALL TessBaseAPISetOutputName(TessBaseAPI* handle, const char* name); TESS_API BOOL TESS_CALL TessBaseAPISetVariable(TessBaseAPI* handle, const char* name, const char* value); TESS_API BOOL TESS_CALL TessBaseAPISetDebugVariable(TessBaseAPI* handle, const char* name, const char* value); TESS_API BOOL TESS_CALL TessBaseAPIGetIntVariable( const TessBaseAPI* handle, const char* name, int* value); TESS_API BOOL TESS_CALL TessBaseAPIGetBoolVariable( const TessBaseAPI* handle, const char* name, BOOL* value); TESS_API BOOL TESS_CALL TessBaseAPIGetDoubleVariable(const TessBaseAPI* handle, const char* name, double* value); TESS_API const char* TESS_CALL TessBaseAPIGetStringVariable(const TessBaseAPI* handle, const char* name); TESS_API void TESS_CALL TessBaseAPIPrintVariables( const TessBaseAPI* handle, FILE* fp); TESS_API BOOL TESS_CALL TessBaseAPIPrintVariablesToFile(const TessBaseAPI* handle, const char* filename); #ifdef TESS_CAPI_INCLUDE_BASEAPI TESS_API BOOL TESS_CALL TessBaseAPIGetVariableAsString(TessBaseAPI* handle, const char* name, STRING* val); #endif #ifdef TESS_CAPI_INCLUDE_BASEAPI TESS_API int TESS_CALL TessBaseAPIInit(TessBaseAPI* handle, const char* datapath, const char* language, TessOcrEngineMode mode, char** configs, int configs_size, const STRING* vars_vec, size_t vars_vec_size, const STRING* vars_values, size_t vars_values_size, BOOL set_only_init_params); #endif TESS_API int TESS_CALL TessBaseAPIInit1(TessBaseAPI* handle, const char* datapath, const char* language, TessOcrEngineMode oem, char** configs, int configs_size); TESS_API int TESS_CALL TessBaseAPIInit2(TessBaseAPI* handle, const char* datapath, const char* language, TessOcrEngineMode oem); TESS_API int TESS_CALL TessBaseAPIInit3(TessBaseAPI* handle, const char* datapath, const char* language); TESS_API int TESS_CALL TessBaseAPIInit4(TessBaseAPI* handle, const char* datapath, const char* language, TessOcrEngineMode mode, char** configs, int configs_size, char** vars_vec, char** vars_values, size_t vars_vec_size, BOOL set_only_non_debug_params); TESS_API const char* TESS_CALL TessBaseAPIGetInitLanguagesAsString(const TessBaseAPI* handle); TESS_API char** TESS_CALL TessBaseAPIGetLoadedLanguagesAsVector(const TessBaseAPI* handle); TESS_API char** TESS_CALL TessBaseAPIGetAvailableLanguagesAsVector(const TessBaseAPI* handle); TESS_API int TESS_CALL TessBaseAPIInitLangMod(TessBaseAPI* handle, const char* datapath, const char* language); TESS_API void TESS_CALL TessBaseAPIInitForAnalysePage(TessBaseAPI* handle); TESS_API void TESS_CALL TessBaseAPIReadConfigFile(TessBaseAPI* handle, const char* filename); TESS_API void TESS_CALL TessBaseAPIReadDebugConfigFile(TessBaseAPI* handle, const char* filename); TESS_API void TESS_CALL TessBaseAPISetPageSegMode(TessBaseAPI* handle, TessPageSegMode mode); TESS_API TessPageSegMode TESS_CALL TessBaseAPIGetPageSegMode(const TessBaseAPI* handle); TESS_API char* TESS_CALL TessBaseAPIRect(TessBaseAPI* handle, const unsigned char* imagedata, int bytes_per_pixel, int bytes_per_line, int left, int top, int width, int height); TESS_API void TESS_CALL TessBaseAPIClearAdaptiveClassifier(TessBaseAPI* handle); TESS_API void TESS_CALL TessBaseAPISetImage(TessBaseAPI* handle, const unsigned char* imagedata, int width, int height, int bytes_per_pixel, int bytes_per_line); TESS_API void TESS_CALL TessBaseAPISetImage2(TessBaseAPI* handle, struct Pix* pix); TESS_API void TESS_CALL TessBaseAPISetSourceResolution(TessBaseAPI* handle, int ppi); TESS_API void TESS_CALL TessBaseAPISetRectangle(TessBaseAPI* handle, int left, int top, int width, int height); #ifdef TESS_CAPI_INCLUDE_BASEAPI TESS_API void TESS_CALL TessBaseAPISetThresholder(TessBaseAPI* handle, TessImageThresholder* thresholder); #endif TESS_API struct Pix* TESS_CALL TessBaseAPIGetThresholdedImage( TessBaseAPI* handle); TESS_API struct Boxa* TESS_CALL TessBaseAPIGetRegions( TessBaseAPI* handle, struct Pixa** pixa); TESS_API struct Boxa* TESS_CALL TessBaseAPIGetTextlines( TessBaseAPI* handle, struct Pixa** pixa, int** blockids); TESS_API struct Boxa* TESS_CALL TessBaseAPIGetTextlines1( TessBaseAPI* handle, const BOOL raw_image, const int raw_padding, struct Pixa** pixa, int** blockids, int** paraids); TESS_API struct Boxa* TESS_CALL TessBaseAPIGetStrips( TessBaseAPI* handle, struct Pixa** pixa, int** blockids); TESS_API struct Boxa* TESS_CALL TessBaseAPIGetWords( TessBaseAPI* handle, struct Pixa** pixa); TESS_API struct Boxa* TESS_CALL TessBaseAPIGetConnectedComponents(TessBaseAPI* handle, struct Pixa** cc); TESS_API struct Boxa* TESS_CALL TessBaseAPIGetComponentImages( TessBaseAPI* handle, const TessPageIteratorLevel level, const BOOL text_only, struct Pixa** pixa, int** blockids); TESS_API struct Boxa* TESS_CALL TessBaseAPIGetComponentImages1( TessBaseAPI* handle, const TessPageIteratorLevel level, const BOOL text_only, const BOOL raw_image, const int raw_padding, struct Pixa** pixa, int** blockids, int** paraids); TESS_API int TESS_CALL TessBaseAPIGetThresholdedImageScaleFactor(const TessBaseAPI* handle); TESS_API void TESS_CALL TessBaseAPIDumpPGM(TessBaseAPI* handle, const char* filename); TESS_API TessPageIterator* TESS_CALL TessBaseAPIAnalyseLayout(TessBaseAPI* handle); TESS_API int TESS_CALL TessBaseAPIRecognize(TessBaseAPI* handle, ETEXT_DESC* monitor); TESS_API int TESS_CALL TessBaseAPIRecognizeForChopTest(TessBaseAPI* handle, ETEXT_DESC* monitor); TESS_API BOOL TESS_CALL TessBaseAPIProcessPages(TessBaseAPI* handle, const char* filename, const char* retry_config, int timeout_millisec, TessResultRenderer* renderer); TESS_API BOOL TESS_CALL TessBaseAPIProcessPage(TessBaseAPI* handle, struct Pix* pix, int page_index, const char* filename, const char* retry_config, int timeout_millisec, TessResultRenderer* renderer); TESS_API TessResultIterator* TESS_CALL TessBaseAPIGetIterator(TessBaseAPI* handle); TESS_API TessMutableIterator* TESS_CALL TessBaseAPIGetMutableIterator(TessBaseAPI* handle); TESS_API char* TESS_CALL TessBaseAPIGetUTF8Text(TessBaseAPI* handle); TESS_API char* TESS_CALL TessBaseAPIGetHOCRText(TessBaseAPI* handle, int page_number); TESS_API char* TESS_CALL TessBaseAPIGetBoxText(TessBaseAPI* handle, int page_number); TESS_API char* TESS_CALL TessBaseAPIGetUNLVText(TessBaseAPI* handle); TESS_API int TESS_CALL TessBaseAPIMeanTextConf(TessBaseAPI* handle); TESS_API int* TESS_CALL TessBaseAPIAllWordConfidences(TessBaseAPI* handle); TESS_API BOOL TESS_CALL TessBaseAPIAdaptToWordStr(TessBaseAPI* handle, TessPageSegMode mode, const char* wordstr); TESS_API void TESS_CALL TessBaseAPIClear(TessBaseAPI* handle); TESS_API void TESS_CALL TessBaseAPIEnd(TessBaseAPI* handle); TESS_API int TESS_CALL TessBaseAPIIsValidWord(TessBaseAPI* handle, const char* word); TESS_API BOOL TESS_CALL TessBaseAPIGetTextDirection(TessBaseAPI* handle, int* out_offset, float* out_slope); #ifdef TESS_CAPI_INCLUDE_BASEAPI TESS_API void TESS_CALL TessBaseAPISetDictFunc(TessBaseAPI* handle, TessDictFunc f); TESS_API void TESS_CALL TessBaseAPIClearPersistentCache(TessBaseAPI* handle); TESS_API void TESS_CALL TessBaseAPISetProbabilityInContextFunc(TessBaseAPI* handle, TessProbabilityInContextFunc f); 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, 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, int num_max_matches, int* unichar_ids, float* ratings, int* num_matches_returned); #endif TESS_API const char* TESS_CALL TessBaseAPIGetUnichar(TessBaseAPI* handle, int unichar_id); #ifdef TESS_CAPI_INCLUDE_BASEAPI TESS_API const TessDawg* TESS_CALL TessBaseAPIGetDawg(const TessBaseAPI* handle, int i); TESS_API int TESS_CALL TessBaseAPINumDawgs(const TessBaseAPI* handle); #endif #ifdef TESS_CAPI_INCLUDE_BASEAPI 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); TESS_API TessOcrEngineMode TESS_CALL TessBaseAPIOem(const TessBaseAPI* handle); TESS_API void TESS_CALL TessBaseAPIInitTruthCallback(TessBaseAPI* handle, TessTruthCallback* cb); #ifndef NO_CUBE_BUILD TESS_API TessCubeRecoContext* TESS_CALL TessBaseAPIGetCubeRecoContext(const TessBaseAPI* handle); #endif // NO_CUBE_BUILD #endif TESS_API void TESS_CALL TessBaseAPISetMinOrientationMargin(TessBaseAPI* handle, double margin); #ifdef TESS_CAPI_INCLUDE_BASEAPI TESS_API void TESS_CALL TessBaseGetBlockTextOrientations(TessBaseAPI* handle, int** block_orientation, BOOL** vertical_writing); TESS_API BLOCK_LIST* TESS_CALL TessBaseAPIFindLinesCreateBlockList(TessBaseAPI* handle); #endif /* Page iterator */ TESS_API void TESS_CALL TessPageIteratorDelete(TessPageIterator* handle); TESS_API TessPageIterator* TESS_CALL TessPageIteratorCopy(const TessPageIterator* handle); TESS_API void TESS_CALL TessPageIteratorBegin(TessPageIterator* handle); TESS_API BOOL TESS_CALL TessPageIteratorNext(TessPageIterator* handle, TessPageIteratorLevel level); TESS_API BOOL TESS_CALL TessPageIteratorIsAtBeginningOf(const TessPageIterator* handle, TessPageIteratorLevel level); TESS_API BOOL TESS_CALL TessPageIteratorIsAtFinalElement(const TessPageIterator* handle, TessPageIteratorLevel level, TessPageIteratorLevel element); TESS_API BOOL TESS_CALL TessPageIteratorBoundingBox(const TessPageIterator* handle, TessPageIteratorLevel level, int* left, int* top, int* right, int* bottom); TESS_API TessPolyBlockType TESS_CALL TessPageIteratorBlockType(const TessPageIterator* handle); TESS_API struct Pix* TESS_CALL TessPageIteratorGetBinaryImage(const TessPageIterator* handle, TessPageIteratorLevel level); TESS_API struct Pix* TESS_CALL TessPageIteratorGetImage(const TessPageIterator* handle, TessPageIteratorLevel level, int padding, struct Pix* original_image, int* left, int* top); TESS_API BOOL TESS_CALL TessPageIteratorBaseline(const TessPageIterator* handle, TessPageIteratorLevel level, int* x1, int* y1, int* x2, int* y2); TESS_API void TESS_CALL TessPageIteratorOrientation(TessPageIterator* handle, TessOrientation* orientation, TessWritingDirection* writing_direction, TessTextlineOrder* textline_order, float* deskew_angle); TESS_API void TESS_CALL TessPageIteratorParagraphInfo(TessPageIterator* handle, TessParagraphJustification* justification, BOOL *is_list_item, BOOL *is_crown, int *first_line_indent); /* Result iterator */ TESS_API void TESS_CALL TessResultIteratorDelete(TessResultIterator* handle); TESS_API TessResultIterator* TESS_CALL TessResultIteratorCopy(const TessResultIterator* handle); TESS_API TessPageIterator* TESS_CALL TessResultIteratorGetPageIterator(TessResultIterator* handle); TESS_API const TessPageIterator* TESS_CALL TessResultIteratorGetPageIteratorConst(const TessResultIterator* handle); TESS_API TessChoiceIterator* TESS_CALL TessResultIteratorGetChoiceIterator(const TessResultIterator* handle); TESS_API BOOL TESS_CALL TessResultIteratorNext(TessResultIterator* handle, TessPageIteratorLevel level); TESS_API char* TESS_CALL TessResultIteratorGetUTF8Text(const TessResultIterator* handle, TessPageIteratorLevel level); TESS_API float TESS_CALL TessResultIteratorConfidence(const TessResultIterator* handle, TessPageIteratorLevel level); TESS_API const char* TESS_CALL TessResultIteratorWordRecognitionLanguage(const TessResultIterator* handle); TESS_API const char* TESS_CALL TessResultIteratorWordFontAttributes(const TessResultIterator* handle, BOOL* is_bold, BOOL* is_italic, BOOL* is_underlined, BOOL* is_monospace, BOOL* is_serif, BOOL* is_smallcaps, int* pointsize, int* font_id); TESS_API BOOL TESS_CALL TessResultIteratorWordIsFromDictionary(const TessResultIterator* handle); TESS_API BOOL TESS_CALL TessResultIteratorWordIsNumeric(const TessResultIterator* handle); TESS_API BOOL TESS_CALL TessResultIteratorSymbolIsSuperscript(const TessResultIterator* handle); TESS_API BOOL TESS_CALL TessResultIteratorSymbolIsSubscript(const TessResultIterator* handle); TESS_API BOOL TESS_CALL TessResultIteratorSymbolIsDropcap(const TessResultIterator* handle); TESS_API void TESS_CALL TessChoiceIteratorDelete(TessChoiceIterator* handle); TESS_API BOOL TESS_CALL TessChoiceIteratorNext(TessChoiceIterator* handle); TESS_API const char* TESS_CALL TessChoiceIteratorGetUTF8Text(const TessChoiceIterator* handle); TESS_API float TESS_CALL TessChoiceIteratorConfidence(const TessChoiceIterator* handle); #ifdef __cplusplus } #endif #endif /* TESSERACT_API_CAPI_H__ */ tesseract-3.04.01/api/pdfrenderer.cpp000066400000000000000000001004231266071204500174340ustar00rootroot00000000000000// Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "baseapi.h" #include "renderer.h" #include "math.h" #include "strngs.h" #include "tprintf.h" #include "allheaders.h" #ifdef _MSC_VER #include "mathfix.h" #endif /* Design notes from Ken Sharp, with light editing. We think one solution is a font with a single glyph (.notdef) and a CIDToGIDMap which maps all the CIDs to 0. That map would then be stored as a stream in the PDF file, and when flate compressed should be pretty small. The font, of course, will be approximately the same size as the one you currently use. I'm working on such a font now, the CIDToGIDMap is trivial, you just create a stream object which contains 128k bytes (2 bytes per possible CID and your CIDs range from 0 to 65535) and where you currently have "/CIDToGIDMap /Identity" you would have "/CIDToGIDMap 0 R". Note that if, in future, you were to use a different (ie not 2 byte) CMap for character codes you could trivially extend the CIDToGIDMap. The following is an explanation of how some of the font stuff works, this may be too simple for you in which case please accept my apologies, its hard to know how much knowledge someone has. You can skip all this anyway, its just for information. The font embedded in a PDF file is usually intended just to be rendered, but extensions allow for at least some ability to locate (or copy) text from a document. This isn't something which was an original goal of the PDF format, but its been retro-fitted, presumably due to popular demand. To do this reliably the PDF file must contain a ToUnicode CMap, a device for mapping character codes to Unicode code points. If one of these is present, then this will be used to convert the character codes into Unicode values. If its not present then the reader will fall back through a series of heuristics to try and guess the result. This is, as you would expect, prone to failure. This doesn't concern you of course, since you always write a ToUnicode CMap, so because you are writing the text in text rendering mode 3 it would seem that you don't really need to worry about this, but in the PDF spec you cannot have an isolated ToUnicode CMap, it has to be attached to a font, so in order to get even copy/paste to work you need to define a font. This is what leads to problems, tools like pdfwrite assume that they are going to be able to (or even have to) modify the font entries, so they require that the font being embedded be valid, and to be honest the font Tesseract embeds isn't valid (for this purpose). To see why lets look at how text is specified in a PDF file: (Test) Tj Now that looks like text but actually it isn't. Each of those bytes is a 'character code'. When it comes to rendering the text a complex sequence of events takes place, which converts the character code into 'something' which the font understands. Its entirely possible via character mappings to have that text render as 'Sftu' For simple fonts (PostScript type 1), we use the character code as the index into an Encoding array (256 elements), each element of which is a glyph name, so this gives us a glyph name. We then consult the CharStrings dictionary in the font, that's a complex object which contains pairs of keys and values, you can use the key to retrieve a given value. So we have a glyph name, we then use that as the key to the dictionary and retrieve the associated value. For a type 1 font, the value is a glyph program that describes how to draw the glyph. For CIDFonts, its a little more complicated. Because CIDFonts can be large, using a glyph name as the key is unreasonable (it would also lead to unfeasibly large Encoding arrays), so instead we use a 'CID' as the key. CIDs are just numbers. But.... We don't use the character code as the CID. What we do is use a CMap to convert the character code into a CID. We then use the CID to key the CharStrings dictionary and proceed as before. So the 'CMap' is the equivalent of the Encoding array, but its a more compact and flexible representation. Note that you have to use the CMap just to find out how many bytes constitute a character code, and it can be variable. For example you can say if the first byte is 0x00->0x7f then its just one byte, if its 0x80->0xf0 then its 2 bytes and if its 0xf0->0xff then its 3 bytes. I have seen CMaps defining character codes up to 5 bytes wide. Now that's fine for 'PostScript' CIDFonts, but its not sufficient for TrueType CIDFonts. The thing is that TrueType fonts are accessed using a Glyph ID (GID) (and the LOCA table) which may well not be anything like the CID. So for this case PDF includes a CIDToGIDMap. That maps the CIDs to GIDs, and we can then use the GID to get the glyph description from the GLYF table of the font. So for a TrueType CIDFont, character-code->CID->GID->glyf-program. Looking at the PDF file I was supplied with we see that it contains text like : <0x0075> Tj So we start by taking the character code (117) and look it up in the CMap. Well you don't supply a CMap, you just use the Identity-H one which is predefined. So character code 117 maps to CID 117. Then we use the CIDToGIDMap, again you don't supply one, you just use the predefined 'Identity' map. So CID 117 maps to GID 117. But the font we were supplied with only contains 116 glyphs. Now for Latin that's not a huge problem, you can just supply a bigger font. But for more complex languages that *is* going to be more of a problem. Either you need to supply a font which contains glyphs for all the possible CID->GID mappings, or we need to think laterally. Our solution using a TrueType CIDFont is to intervene at the CIDToGIDMap stage and convert all the CIDs to GID 0. Then we have a font with just one glyph, the .notdef glyph at GID 0. This is what I'm looking into now. It would also be possible to have a 'PostScript' (ie type 1 outlines) CIDFont which contained 1 glyph, and a CMap which mapped all character codes to CID 0. The effect would be the same. Its possible (I haven't checked) that the PostScript CIDFont and associated CMap would be smaller than the TrueType font and associated CIDToGIDMap. --- in a followup --- OK there is a small problem there, if I use GID 0 then Acrobat gets upset about it and complains it cannot extract the font. If I set the CIDToGIDMap so that all the entries are 1 instead, its happy. Totally mad...... */ namespace tesseract { // Use for PDF object fragments. Must be large enough // to hold a colormap with 256 colors in the verbose // PDF representation. const int kBasicBufSize = 2048; // If the font is 10 pts, nominal character width is 5 pts const int kCharWidth = 2; /********************************************************************** * PDF Renderer interface implementation **********************************************************************/ TessPDFRenderer::TessPDFRenderer(const char* outputbase, const char *datadir) : TessResultRenderer(outputbase, "pdf") { obj_ = 0; datadir_ = datadir; offsets_.push_back(0); } void TessPDFRenderer::AppendPDFObjectDIY(size_t objectsize) { offsets_.push_back(objectsize + offsets_.back()); obj_++; } void TessPDFRenderer::AppendPDFObject(const char *data) { AppendPDFObjectDIY(strlen(data)); AppendString((const char *)data); } // Helper function to prevent us from accidentally writing // scientific notation to an HOCR or PDF file. Besides, three // decimal points are all you really need. double prec(double x) { double kPrecision = 1000.0; double a = round(x * kPrecision) / kPrecision; if (a == -0) return 0; return a; } long dist2(int x1, int y1, int x2, int y2) { return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); } // Viewers like evince can get really confused during copy-paste when // the baseline wanders around. So I've decided to project every word // onto the (straight) line baseline. All numbers are in the native // PDF coordinate system, which has the origin in the bottom left and // the unit is points, which is 1/72 inch. Tesseract reports baselines // left-to-right no matter what the reading order is. We need the // word baseline in reading order, so we do that conversion here. Returns // the word's baseline origin and length. void GetWordBaseline(int writing_direction, int ppi, int height, int word_x1, int word_y1, int word_x2, int word_y2, int line_x1, int line_y1, int line_x2, int line_y2, double *x0, double *y0, double *length) { if (writing_direction == WRITING_DIRECTION_RIGHT_TO_LEFT) { Swap(&word_x1, &word_x2); Swap(&word_y1, &word_y2); } double word_length; double x, y; { int px = word_x1; int py = word_y1; double l2 = dist2(line_x1, line_y1, line_x2, line_y2); if (l2 == 0) { x = line_x1; y = line_y1; } else { double t = ((px - line_x2) * (line_x2 - line_x1) + (py - line_y2) * (line_y2 - line_y1)) / l2; x = line_x2 + t * (line_x2 - line_x1); y = line_y2 + t * (line_y2 - line_y1); } word_length = sqrt(static_cast(dist2(word_x1, word_y1, word_x2, word_y2))); word_length = word_length * 72.0 / ppi; x = x * 72 / ppi; y = height - (y * 72.0 / ppi); } *x0 = x; *y0 = y; *length = word_length; } // Compute coefficients for an affine matrix describing the rotation // of the text. If the text is right-to-left such as Arabic or Hebrew, // we reflect over the Y-axis. This matrix will set the coordinate // system for placing text in the PDF file. // // RTL // [ x' ] = [ a b ][ x ] = [-1 0 ] [ cos sin ][ x ] // [ y' ] [ c d ][ y ] [ 0 1 ] [-sin cos ][ y ] void AffineMatrix(int writing_direction, int line_x1, int line_y1, int line_x2, int line_y2, double *a, double *b, double *c, double *d) { double theta = atan2(static_cast(line_y1 - line_y2), static_cast(line_x2 - line_x1)); *a = cos(theta); *b = sin(theta); *c = -sin(theta); *d = cos(theta); switch(writing_direction) { case WRITING_DIRECTION_RIGHT_TO_LEFT: *a = -*a; *b = -*b; break; case WRITING_DIRECTION_TOP_TO_BOTTOM: // TODO(jbreiden) Consider using the vertical PDF writing mode. break; default: break; } } // There are some really stupid PDF viewers in the wild, such as // 'Preview' which ships with the Mac. They do a better job with text // selection and highlighting when given perfectly flat baseline // instead of very slightly tilted. We clip small tilts to appease // these viewers. I chose this threshold large enough to absorb noise, // but small enough that lines probably won't cross each other if the // whole page is tilted at almost exactly the clipping threshold. void ClipBaseline(int ppi, int x1, int y1, int x2, int y2, int *line_x1, int *line_y1, int *line_x2, int *line_y2) { *line_x1 = x1; *line_y1 = y1; *line_x2 = x2; *line_y2 = y2; double rise = abs(y2 - y1) * 72 / ppi; double run = abs(x2 - x1) * 72 / ppi; if (rise < 2.0 && 2.0 < run) *line_y1 = *line_y2 = (y1 + y2) / 2; } char* TessPDFRenderer::GetPDFTextObjects(TessBaseAPI* api, double width, double height) { STRING pdf_str(""); double ppi = api->GetSourceYResolution(); // These initial conditions are all arbitrary and will be overwritten double old_x = 0.0, old_y = 0.0; int old_fontsize = 0; tesseract::WritingDirection old_writing_direction = WRITING_DIRECTION_LEFT_TO_RIGHT; bool new_block = true; int fontsize = 0; double a = 1; double b = 0; double c = 0; double d = 1; // TODO(jbreiden) This marries the text and image together. // Slightly cleaner from an abstraction standpoint if this were to // live inside a separate text object. pdf_str += "q "; pdf_str.add_str_double("", prec(width)); pdf_str += " 0 0 "; pdf_str.add_str_double("", prec(height)); pdf_str += " 0 0 cm /Im1 Do Q\n"; int line_x1 = 0; int line_y1 = 0; int line_x2 = 0; int line_y2 = 0; ResultIterator *res_it = api->GetIterator(); while (!res_it->Empty(RIL_BLOCK)) { if (res_it->IsAtBeginningOf(RIL_BLOCK)) { pdf_str += "BT\n3 Tr"; // Begin text object, use invisible ink old_fontsize = 0; // Every block will declare its fontsize new_block = true; // Every block will declare its affine matrix } if (res_it->IsAtBeginningOf(RIL_TEXTLINE)) { int x1, y1, x2, y2; res_it->Baseline(RIL_TEXTLINE, &x1, &y1, &x2, &y2); ClipBaseline(ppi, x1, y1, x2, y2, &line_x1, &line_y1, &line_x2, &line_y2); } if (res_it->Empty(RIL_WORD)) { res_it->Next(RIL_WORD); continue; } // Writing direction changes at a per-word granularity tesseract::WritingDirection writing_direction; { tesseract::Orientation orientation; tesseract::TextlineOrder textline_order; float deskew_angle; res_it->Orientation(&orientation, &writing_direction, &textline_order, &deskew_angle); if (writing_direction != WRITING_DIRECTION_TOP_TO_BOTTOM) { switch (res_it->WordDirection()) { case DIR_LEFT_TO_RIGHT: writing_direction = WRITING_DIRECTION_LEFT_TO_RIGHT; break; case DIR_RIGHT_TO_LEFT: writing_direction = WRITING_DIRECTION_RIGHT_TO_LEFT; break; default: writing_direction = old_writing_direction; } } } // Where is word origin and how long is it? double x, y, word_length; { int word_x1, word_y1, word_x2, word_y2; res_it->Baseline(RIL_WORD, &word_x1, &word_y1, &word_x2, &word_y2); GetWordBaseline(writing_direction, ppi, height, word_x1, word_y1, word_x2, word_y2, line_x1, line_y1, line_x2, line_y2, &x, &y, &word_length); } if (writing_direction != old_writing_direction || new_block) { AffineMatrix(writing_direction, line_x1, line_y1, line_x2, line_y2, &a, &b, &c, &d); pdf_str.add_str_double(" ", prec(a)); // . This affine matrix pdf_str.add_str_double(" ", prec(b)); // . sets the coordinate pdf_str.add_str_double(" ", prec(c)); // . system for all pdf_str.add_str_double(" ", prec(d)); // . text that follows. pdf_str.add_str_double(" ", prec(x)); // . pdf_str.add_str_double(" ", prec(y)); // . pdf_str += (" Tm "); // Place cursor absolutely new_block = false; } else { double dx = x - old_x; double dy = y - old_y; pdf_str.add_str_double(" ", prec(dx * a + dy * b)); pdf_str.add_str_double(" ", prec(dx * c + dy * d)); pdf_str += (" Td "); // Relative moveto } old_x = x; old_y = y; old_writing_direction = writing_direction; // Adjust font size on a per word granularity. Pay attention to // fontsize, old_fontsize, and pdf_str. We've found that for // in Arabic, Tesseract will happily return a fontsize of zero, // so we make up a default number to protect ourselves. { bool bold, italic, underlined, monospace, serif, smallcaps; int font_id; res_it->WordFontAttributes(&bold, &italic, &underlined, &monospace, &serif, &smallcaps, &fontsize, &font_id); const int kDefaultFontsize = 8; if (fontsize <= 0) fontsize = kDefaultFontsize; if (fontsize != old_fontsize) { char textfont[20]; snprintf(textfont, sizeof(textfont), "/f-0-0 %d Tf ", fontsize); pdf_str += textfont; old_fontsize = fontsize; } } bool last_word_in_line = res_it->IsAtFinalElement(RIL_TEXTLINE, RIL_WORD); bool last_word_in_block = res_it->IsAtFinalElement(RIL_BLOCK, RIL_WORD); STRING pdf_word(""); int pdf_word_len = 0; do { const char *grapheme = res_it->GetUTF8Text(RIL_SYMBOL); if (grapheme && grapheme[0] != '\0') { GenericVector unicodes; UNICHAR::UTF8ToUnicode(grapheme, &unicodes); char utf16[20]; for (int i = 0; i < unicodes.length(); i++) { int code = unicodes[i]; // Convert to UTF-16BE https://en.wikipedia.org/wiki/UTF-16 if ((code > 0xD7FF && code < 0xE000) || code > 0x10FFFF) { tprintf("Dropping invalid codepoint %d\n", code); continue; } if (code < 0x10000) { snprintf(utf16, sizeof(utf16), "<%04X>", code); } else { int a = code - 0x010000; int high_surrogate = (0x03FF & (a >> 10)) + 0xD800; int low_surrogate = (0x03FF & a) + 0xDC00; snprintf(utf16, sizeof(utf16), "<%04X%04X>", high_surrogate, low_surrogate); } pdf_word += utf16; pdf_word_len++; } } delete []grapheme; res_it->Next(RIL_SYMBOL); } while (!res_it->Empty(RIL_BLOCK) && !res_it->IsAtBeginningOf(RIL_WORD)); if (word_length > 0 && pdf_word_len > 0 && fontsize > 0) { double h_stretch = kCharWidth * prec(100.0 * word_length / (fontsize * pdf_word_len)); pdf_str.add_str_double("", h_stretch); pdf_str += " Tz"; // horizontal stretch pdf_str += " [ "; pdf_str += pdf_word; // UTF-16BE representation pdf_str += " ] TJ"; // show the text } if (last_word_in_line) { pdf_str += " \n"; } if (last_word_in_block) { pdf_str += "ET\n"; // end the text object } } char *ret = new char[pdf_str.length() + 1]; strcpy(ret, pdf_str.string()); delete res_it; return ret; } bool TessPDFRenderer::BeginDocumentHandler() { char buf[kBasicBufSize]; size_t n; n = snprintf(buf, sizeof(buf), "%%PDF-1.5\n" "%%%c%c%c%c\n", 0xDE, 0xAD, 0xBE, 0xEB); if (n >= sizeof(buf)) return false; AppendPDFObject(buf); // CATALOG n = snprintf(buf, sizeof(buf), "1 0 obj\n" "<<\n" " /Type /Catalog\n" " /Pages %ld 0 R\n" ">>\n" "endobj\n", 2L); if (n >= sizeof(buf)) return false; AppendPDFObject(buf); // We are reserving object #2 for the /Pages // object, which I am going to create and write // at the end of the PDF file. AppendPDFObject(""); // TYPE0 FONT n = snprintf(buf, sizeof(buf), "3 0 obj\n" "<<\n" " /BaseFont /GlyphLessFont\n" " /DescendantFonts [ %ld 0 R ]\n" " /Encoding /Identity-H\n" " /Subtype /Type0\n" " /ToUnicode %ld 0 R\n" " /Type /Font\n" ">>\n" "endobj\n", 4L, // CIDFontType2 font 6L // ToUnicode ); if (n >= sizeof(buf)) return false; AppendPDFObject(buf); // CIDFONTTYPE2 n = snprintf(buf, sizeof(buf), "4 0 obj\n" "<<\n" " /BaseFont /GlyphLessFont\n" " /CIDToGIDMap %ld 0 R\n" " /CIDSystemInfo\n" " <<\n" " /Ordering (Identity)\n" " /Registry (Adobe)\n" " /Supplement 0\n" " >>\n" " /FontDescriptor %ld 0 R\n" " /Subtype /CIDFontType2\n" " /Type /Font\n" " /DW %d\n" ">>\n" "endobj\n", 5L, // CIDToGIDMap 7L, // Font descriptor 1000 / kCharWidth); if (n >= sizeof(buf)) return false; AppendPDFObject(buf); // CIDTOGIDMAP const int kCIDToGIDMapSize = 2 * (1 << 16); unsigned char *cidtogidmap = new unsigned char[kCIDToGIDMapSize]; for (int i = 0; i < kCIDToGIDMapSize; i++) { cidtogidmap[i] = (i % 2) ? 1 : 0; } size_t len; unsigned char *comp = zlibCompress(cidtogidmap, kCIDToGIDMapSize, &len); delete[] cidtogidmap; n = snprintf(buf, sizeof(buf), "5 0 obj\n" "<<\n" " /Length %lu /Filter /FlateDecode\n" ">>\n" "stream\n", (unsigned long)len); if (n >= sizeof(buf)) { lept_free(comp); return false; } AppendString(buf); long objsize = strlen(buf); AppendData(reinterpret_cast(comp), len); objsize += len; lept_free(comp); const char *endstream_endobj = "endstream\n" "endobj\n"; AppendString(endstream_endobj); objsize += strlen(endstream_endobj); AppendPDFObjectDIY(objsize); const char *stream = "/CIDInit /ProcSet findresource begin\n" "12 dict begin\n" "begincmap\n" "/CIDSystemInfo\n" "<<\n" " /Registry (Adobe)\n" " /Ordering (UCS)\n" " /Supplement 0\n" ">> def\n" "/CMapName /Adobe-Identify-UCS def\n" "/CMapType 2 def\n" "1 begincodespacerange\n" "<0000> \n" "endcodespacerange\n" "1 beginbfrange\n" "<0000> <0000>\n" "endbfrange\n" "endcmap\n" "CMapName currentdict /CMap defineresource pop\n" "end\n" "end\n"; // TOUNICODE n = snprintf(buf, sizeof(buf), "6 0 obj\n" "<< /Length %lu >>\n" "stream\n" "%s" "endstream\n" "endobj\n", (unsigned long) strlen(stream), stream); if (n >= sizeof(buf)) return false; AppendPDFObject(buf); // FONT DESCRIPTOR const int kCharHeight = 2; // Effect: highlights are half height n = snprintf(buf, sizeof(buf), "7 0 obj\n" "<<\n" " /Ascent %d\n" " /CapHeight %d\n" " /Descent -1\n" // Spec says must be negative " /Flags 5\n" // FixedPitch + Symbolic " /FontBBox [ 0 0 %d %d ]\n" " /FontFile2 %ld 0 R\n" " /FontName /GlyphLessFont\n" " /ItalicAngle 0\n" " /StemV 80\n" " /Type /FontDescriptor\n" ">>\n" "endobj\n", 1000 / kCharHeight, 1000 / kCharHeight, 1000 / kCharWidth, 1000 / kCharHeight, 8L // Font data ); if (n >= sizeof(buf)) return false; AppendPDFObject(buf); n = snprintf(buf, sizeof(buf), "%s/pdf.ttf", datadir_); if (n >= sizeof(buf)) return false; FILE *fp = fopen(buf, "rb"); if (!fp) { tprintf("Can not open file \"%s\"!\n", buf); return false; } fseek(fp, 0, SEEK_END); long int size = ftell(fp); fseek(fp, 0, SEEK_SET); char *buffer = new char[size]; if (fread(buffer, 1, size, fp) != size) { fclose(fp); delete[] buffer; return false; } fclose(fp); // FONTFILE2 n = snprintf(buf, sizeof(buf), "8 0 obj\n" "<<\n" " /Length %ld\n" " /Length1 %ld\n" ">>\n" "stream\n", size, size); if (n >= sizeof(buf)) { delete[] buffer; return false; } AppendString(buf); objsize = strlen(buf); AppendData(buffer, size); delete[] buffer; objsize += size; AppendString(endstream_endobj); objsize += strlen(endstream_endobj); AppendPDFObjectDIY(objsize); return true; } bool TessPDFRenderer::imageToPDFObj(Pix *pix, char *filename, long int objnum, char **pdf_object, long int *pdf_object_size) { size_t n; char b0[kBasicBufSize]; char b1[kBasicBufSize]; char b2[kBasicBufSize]; if (!pdf_object_size || !pdf_object) return false; *pdf_object = NULL; *pdf_object_size = 0; if (!filename) return false; L_COMP_DATA *cid = NULL; const int kJpegQuality = 85; // TODO(jbreiden) Leptonica 1.71 doesn't correctly handle certain // types of PNG files, especially if there are 2 samples per pixel. // We can get rid of this logic after Leptonica 1.72 is released and // has propagated everywhere. Bug discussion as follows. // https://code.google.com/p/tesseract-ocr/issues/detail?id=1300 int format, sad; findFileFormat(filename, &format); if (pixGetSpp(pix) == 4 && format == IFF_PNG) { pixSetSpp(pix, 3); sad = pixGenerateCIData(pix, L_FLATE_ENCODE, 0, 0, &cid); } else { sad = l_generateCIDataForPdf(filename, pix, kJpegQuality, &cid); } if (sad || !cid) { l_CIDataDestroy(&cid); return false; } const char *group4 = ""; const char *filter; switch(cid->type) { case L_FLATE_ENCODE: filter = "/FlateDecode"; break; case L_JPEG_ENCODE: filter = "/DCTDecode"; break; case L_G4_ENCODE: filter = "/CCITTFaxDecode"; group4 = " /K -1\n"; break; case L_JP2K_ENCODE: filter = "/JPXDecode"; break; default: l_CIDataDestroy(&cid); return false; } // Maybe someday we will accept RGBA but today is not that day. // It requires creating an /SMask for the alpha channel. // http://stackoverflow.com/questions/14220221 const char *colorspace; if (cid->ncolors > 0) { n = snprintf(b0, sizeof(b0), " /ColorSpace [ /Indexed /DeviceRGB %d %s ]\n", cid->ncolors - 1, cid->cmapdatahex); if (n >= sizeof(b0)) { l_CIDataDestroy(&cid); return false; } colorspace = b0; } else { switch (cid->spp) { case 1: colorspace = " /ColorSpace /DeviceGray\n"; break; case 3: colorspace = " /ColorSpace /DeviceRGB\n"; break; default: l_CIDataDestroy(&cid); return false; } } int predictor = (cid->predictor) ? 14 : 1; // IMAGE n = snprintf(b1, sizeof(b1), "%ld 0 obj\n" "<<\n" " /Length %ld\n" " /Subtype /Image\n", objnum, (unsigned long) cid->nbytescomp); if (n >= sizeof(b1)) { l_CIDataDestroy(&cid); return false; } n = snprintf(b2, sizeof(b2), " /Width %d\n" " /Height %d\n" " /BitsPerComponent %d\n" " /Filter %s\n" " /DecodeParms\n" " <<\n" " /Predictor %d\n" " /Colors %d\n" "%s" " /Columns %d\n" " /BitsPerComponent %d\n" " >>\n" ">>\n" "stream\n", cid->w, cid->h, cid->bps, filter, predictor, cid->spp, group4, cid->w, cid->bps); if (n >= sizeof(b2)) { l_CIDataDestroy(&cid); return false; } const char *b3 = "endstream\n" "endobj\n"; size_t b1_len = strlen(b1); size_t b2_len = strlen(b2); size_t b3_len = strlen(b3); size_t colorspace_len = strlen(colorspace); *pdf_object_size = b1_len + colorspace_len + b2_len + cid->nbytescomp + b3_len; *pdf_object = new char[*pdf_object_size]; if (!pdf_object) { l_CIDataDestroy(&cid); return false; } char *p = *pdf_object; memcpy(p, b1, b1_len); p += b1_len; memcpy(p, colorspace, colorspace_len); p += colorspace_len; memcpy(p, b2, b2_len); p += b2_len; memcpy(p, cid->datacomp, cid->nbytescomp); p += cid->nbytescomp; memcpy(p, b3, b3_len); l_CIDataDestroy(&cid); return true; } bool TessPDFRenderer::AddImageHandler(TessBaseAPI* api) { size_t n; char buf[kBasicBufSize]; Pix *pix = api->GetInputImage(); char *filename = (char *)api->GetInputName(); int ppi = api->GetSourceYResolution(); if (!pix || ppi <= 0) return false; double width = pixGetWidth(pix) * 72.0 / ppi; double height = pixGetHeight(pix) * 72.0 / ppi; // PAGE n = snprintf(buf, sizeof(buf), "%ld 0 obj\n" "<<\n" " /Type /Page\n" " /Parent %ld 0 R\n" " /MediaBox [0 0 %.2f %.2f]\n" " /Contents %ld 0 R\n" " /Resources\n" " <<\n" " /XObject << /Im1 %ld 0 R >>\n" " /ProcSet [ /PDF /Text /ImageB /ImageI /ImageC ]\n" " /Font << /f-0-0 %ld 0 R >>\n" " >>\n" ">>\n" "endobj\n", obj_, 2L, // Pages object width, height, obj_ + 1, // Contents object obj_ + 2, // Image object 3L); // Type0 Font if (n >= sizeof(buf)) return false; pages_.push_back(obj_); AppendPDFObject(buf); // CONTENTS char* pdftext = GetPDFTextObjects(api, width, height); long pdftext_len = strlen(pdftext); unsigned char *pdftext_casted = reinterpret_cast(pdftext); size_t len; unsigned char *comp_pdftext = zlibCompress(pdftext_casted, pdftext_len, &len); long comp_pdftext_len = len; n = snprintf(buf, sizeof(buf), "%ld 0 obj\n" "<<\n" " /Length %ld /Filter /FlateDecode\n" ">>\n" "stream\n", obj_, comp_pdftext_len); if (n >= sizeof(buf)) { delete[] pdftext; lept_free(comp_pdftext); return false; } AppendString(buf); long objsize = strlen(buf); AppendData(reinterpret_cast(comp_pdftext), comp_pdftext_len); objsize += comp_pdftext_len; lept_free(comp_pdftext); delete[] pdftext; const char *b2 = "endstream\n" "endobj\n"; AppendString(b2); objsize += strlen(b2); AppendPDFObjectDIY(objsize); char *pdf_object; if (!imageToPDFObj(pix, filename, obj_, &pdf_object, &objsize)) { return false; } AppendData(pdf_object, objsize); AppendPDFObjectDIY(objsize); delete[] pdf_object; return true; } bool TessPDFRenderer::EndDocumentHandler() { size_t n; char buf[kBasicBufSize]; // We reserved the /Pages object number early, so that the /Page // objects could refer to their parent. We finally have enough // information to go fill it in. Using lower level calls to manipulate // the offset record in two spots, because we are placing objects // out of order in the file. // PAGES const long int kPagesObjectNumber = 2; offsets_[kPagesObjectNumber] = offsets_.back(); // manipulation #1 n = snprintf(buf, sizeof(buf), "%ld 0 obj\n" "<<\n" " /Type /Pages\n" " /Kids [ ", kPagesObjectNumber); if (n >= sizeof(buf)) return false; AppendString(buf); size_t pages_objsize = strlen(buf); for (size_t i = 0; i < pages_.size(); i++) { n = snprintf(buf, sizeof(buf), "%ld 0 R ", pages_[i]); if (n >= sizeof(buf)) return false; AppendString(buf); pages_objsize += strlen(buf); } n = snprintf(buf, sizeof(buf), "]\n" " /Count %d\n" ">>\n" "endobj\n", pages_.size()); if (n >= sizeof(buf)) return false; AppendString(buf); pages_objsize += strlen(buf); offsets_.back() += pages_objsize; // manipulation #2 // INFO char* datestr = l_getFormattedDate(); n = snprintf(buf, sizeof(buf), "%ld 0 obj\n" "<<\n" " /Producer (Tesseract %s)\n" " /CreationDate (D:%s)\n" " /Title (%s)" ">>\n" "endobj\n", obj_, TESSERACT_VERSION_STR, datestr, title()); lept_free(datestr); if (n >= sizeof(buf)) return false; AppendPDFObject(buf); n = snprintf(buf, sizeof(buf), "xref\n" "0 %ld\n" "0000000000 65535 f \n", obj_); if (n >= sizeof(buf)) return false; AppendString(buf); for (int i = 1; i < obj_; i++) { n = snprintf(buf, sizeof(buf), "%010ld 00000 n \n", offsets_[i]); if (n >= sizeof(buf)) return false; AppendString(buf); } n = snprintf(buf, sizeof(buf), "trailer\n" "<<\n" " /Size %ld\n" " /Root %ld 0 R\n" " /Info %ld 0 R\n" ">>\n" "startxref\n" "%ld\n" "%%%%EOF\n", obj_, 1L, // catalog obj_ - 1, // info offsets_.back()); if (n >= sizeof(buf)) return false; AppendString(buf); return true; } } // namespace tesseract tesseract-3.04.01/api/renderer.cpp000066400000000000000000000141221266071204500167420ustar00rootroot00000000000000// Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include #include "baseapi.h" #include "genericvector.h" #include "renderer.h" namespace tesseract { /********************************************************************** * Base Renderer interface implementation **********************************************************************/ TessResultRenderer::TessResultRenderer(const char *outputbase, const char* extension) : file_extension_(extension), title_(""), imagenum_(-1), fout_(stdout), next_(NULL), happy_(true) { if (strcmp(outputbase, "-") && strcmp(outputbase, "stdout")) { STRING outfile = STRING(outputbase) + STRING(".") + STRING(file_extension_); fout_ = fopen(outfile.string(), "wb"); if (fout_ == NULL) { happy_ = false; } } } TessResultRenderer::~TessResultRenderer() { if (fout_ != stdout) fclose(fout_); else clearerr(fout_); delete next_; } void TessResultRenderer::insert(TessResultRenderer* next) { if (next == NULL) return; TessResultRenderer* remainder = next_; next_ = next; if (remainder) { while (next->next_ != NULL) { next = next->next_; } next->next_ = remainder; } } bool TessResultRenderer::BeginDocument(const char* title) { if (!happy_) return false; title_ = title; imagenum_ = -1; bool ok = BeginDocumentHandler(); if (next_) { ok = next_->BeginDocument(title) && ok; } return ok; } bool TessResultRenderer::AddImage(TessBaseAPI* api) { if (!happy_) return false; ++imagenum_; bool ok = AddImageHandler(api); if (next_) { ok = next_->AddImage(api) && ok; } return ok; } bool TessResultRenderer::EndDocument() { if (!happy_) return false; bool ok = EndDocumentHandler(); if (next_) { ok = next_->EndDocument() && ok; } return ok; } void TessResultRenderer::AppendString(const char* s) { AppendData(s, strlen(s)); } void TessResultRenderer::AppendData(const char* s, int len) { int n = fwrite(s, 1, len, fout_); if (n != len) happy_ = false; } bool TessResultRenderer::BeginDocumentHandler() { return happy_; } bool TessResultRenderer::EndDocumentHandler() { return happy_; } /********************************************************************** * UTF8 Text Renderer interface implementation **********************************************************************/ TessTextRenderer::TessTextRenderer(const char *outputbase) : TessResultRenderer(outputbase, "txt") { } bool TessTextRenderer::AddImageHandler(TessBaseAPI* api) { char* utf8 = api->GetUTF8Text(); if (utf8 == NULL) { return false; } AppendString(utf8); delete[] utf8; bool pageBreak = false; api->GetBoolVariable("include_page_breaks", &pageBreak); const char* pageSeparator = api->GetStringVariable("page_separator"); if (pageBreak) { AppendString(pageSeparator); } return true; } /********************************************************************** * HOcr Text Renderer interface implementation **********************************************************************/ TessHOcrRenderer::TessHOcrRenderer(const char *outputbase) : TessResultRenderer(outputbase, "hocr") { font_info_ = false; } TessHOcrRenderer::TessHOcrRenderer(const char *outputbase, bool font_info) : TessResultRenderer(outputbase, "hocr") { font_info_ = font_info; } bool TessHOcrRenderer::BeginDocumentHandler() { AppendString( "\n" "\n" "\n \n "); AppendString(title()); AppendString( "\n" "\n" " \n" " \n" "\n\n"); return true; } bool TessHOcrRenderer::EndDocumentHandler() { AppendString(" \n\n"); return true; } bool TessHOcrRenderer::AddImageHandler(TessBaseAPI* api) { char* hocr = api->GetHOCRText(imagenum()); if (hocr == NULL) return false; AppendString(hocr); delete[] hocr; return true; } /********************************************************************** * UNLV Text Renderer interface implementation **********************************************************************/ TessUnlvRenderer::TessUnlvRenderer(const char *outputbase) : TessResultRenderer(outputbase, "unlv") { } bool TessUnlvRenderer::AddImageHandler(TessBaseAPI* api) { char* unlv = api->GetUNLVText(); if (unlv == NULL) return false; AppendString(unlv); delete[] unlv; return true; } /********************************************************************** * BoxText Renderer interface implementation **********************************************************************/ TessBoxTextRenderer::TessBoxTextRenderer(const char *outputbase) : TessResultRenderer(outputbase, "box") { } bool TessBoxTextRenderer::AddImageHandler(TessBaseAPI* api) { char* text = api->GetBoxText(imagenum()); if (text == NULL) return false; AppendString(text); delete[] text; return true; } /********************************************************************** * Osd Text Renderer interface implementation **********************************************************************/ TessOsdRenderer::TessOsdRenderer(const char* outputbase) : TessResultRenderer(outputbase, "osd") { } bool TessOsdRenderer::AddImageHandler(TessBaseAPI* api) { char* osd = api->GetOsdText(imagenum()); if (osd == NULL) return false; AppendString(osd); delete[] osd; return true; } } // namespace tesseract tesseract-3.04.01/api/renderer.h000066400000000000000000000204201266071204500164050ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: renderer.h // Description: Rendering interface to inject into TessBaseAPI // // (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_API_RENDERER_H__ #define TESSERACT_API_RENDERER_H__ // To avoid collision with other typenames include the ABSOLUTE MINIMUM // complexity of includes here. Use forward declarations wherever possible // and hide includes of complex types in baseapi.cpp. #include "genericvector.h" #include "platform.h" #include "publictypes.h" namespace tesseract { class TessBaseAPI; /** * Interface for rendering tesseract results into a document, such as text, * HOCR or pdf. This class is abstract. Specific classes handle individual * formats. This interface is then used to inject the renderer class into * tesseract when processing images. * * For simplicity implementing this with tesesract version 3.01, * the renderer contains document state that is cleared from document * to document just as the TessBaseAPI is. This way the base API can just * delegate its rendering functionality to injected renderers, and the * renderers can manage the associated state needed for the specific formats * in addition to the heuristics for producing it. */ class TESS_API TessResultRenderer { public: virtual ~TessResultRenderer(); // Takes ownership of pointer so must be new'd instance. // Renderers aren't ordered, but appends the sequences of next parameter // and existing next(). The renderers should be unique across both lists. void insert(TessResultRenderer* next); // Returns the next renderer or NULL. TessResultRenderer* next() { return next_; } /** * Starts a new document with the given title. * This clears the contents of the output data. */ bool BeginDocument(const char* title); /** * Adds the recognized text from the source image to the current document. * Invalid if BeginDocument not yet called. * * Note that this API is a bit weird but is designed to fit into the * current TessBaseAPI implementation where the api has lots of state * information that we might want to add in. */ bool AddImage(TessBaseAPI* api); /** * Finishes the document and finalizes the output data * Invalid if BeginDocument not yet called. */ bool EndDocument(); const char* file_extension() const { return file_extension_; } const char* title() const { return title_; } /** * Returns the index of the last image given to AddImage * (i.e. images are incremented whether the image succeeded or not) * * This is always defined. It means either the number of the * current image, the last image ended, or in the completed document * depending on when in the document lifecycle you are looking at it. * Will return -1 if a document was never started. */ int imagenum() const { return imagenum_; } protected: /** * Called by concrete classes. * * outputbase is the name of the output file excluding * extension. For example, "/path/to/chocolate-chip-cookie-recipe" * * extension indicates the file extension to be used for output * files. For example "pdf" will produce a .pdf file, and "hocr" * will produce .hocr files. */ TessResultRenderer(const char *outputbase, const char* extension); // Hook for specialized handling in BeginDocument() virtual bool BeginDocumentHandler(); // This must be overriden to render the OCR'd results virtual bool AddImageHandler(TessBaseAPI* api) = 0; // Hook for specialized handling in EndDocument() virtual bool EndDocumentHandler(); // Renderers can call this to append '\0' terminated strings into // the output string returned by GetOutput. // This method will grow the output buffer if needed. void AppendString(const char* s); // Renderers can call this to append binary byte sequences into // the output string returned by GetOutput. Note that s is not necessarily // '\0' terminated (and can contain '\0' within it). // This method will grow the output buffer if needed. void AppendData(const char* s, int len); private: const char* file_extension_; // standard extension for generated output const char* title_; // title of document being renderered int imagenum_; // index of last image added FILE* fout_; // output file pointer TessResultRenderer* next_; // Can link multiple renderers together bool happy_; // I get grumpy when the disk fills up, etc. }; /** * Renders tesseract output into a plain UTF-8 text string */ class TESS_API TessTextRenderer : public TessResultRenderer { public: explicit TessTextRenderer(const char *outputbase); protected: virtual bool AddImageHandler(TessBaseAPI* api); }; /** * Renders tesseract output into an hocr text string */ class TESS_API TessHOcrRenderer : public TessResultRenderer { public: explicit TessHOcrRenderer(const char *outputbase, bool font_info); explicit TessHOcrRenderer(const char *outputbase); protected: virtual bool BeginDocumentHandler(); virtual bool AddImageHandler(TessBaseAPI* api); virtual bool EndDocumentHandler(); private: bool font_info_; // whether to print font information }; /** * Renders tesseract output into searchable PDF */ class TESS_API TessPDFRenderer : public TessResultRenderer { public: // datadir is the location of the TESSDATA. We need it because // we load a custom PDF font from this location. TessPDFRenderer(const char *outputbase, const char *datadir); protected: virtual bool BeginDocumentHandler(); virtual bool AddImageHandler(TessBaseAPI* api); virtual bool EndDocumentHandler(); private: // We don't want to have every image in memory at once, // so we store some metadata as we go along producing // PDFs one page at a time. At the end that metadata is // used to make everything that isn't easily handled in a // streaming fashion. long int obj_; // counter for PDF objects GenericVector offsets_; // offset of every PDF object in bytes GenericVector pages_; // object number for every /Page object const char *datadir_; // where to find the custom font // Bookkeeping only. DIY = Do It Yourself. void AppendPDFObjectDIY(size_t objectsize); // Bookkeeping + emit data. void AppendPDFObject(const char *data); // Create the /Contents object for an entire page. static char* GetPDFTextObjects(TessBaseAPI* api, double width, double height); // Turn an image into a PDF object. Only transcode if we have to. static bool imageToPDFObj(Pix *pix, char *filename, long int objnum, char **pdf_object, long int *pdf_object_size); }; /** * Renders tesseract output into a plain UTF-8 text string */ class TESS_API TessUnlvRenderer : public TessResultRenderer { public: explicit TessUnlvRenderer(const char *outputbase); protected: virtual bool AddImageHandler(TessBaseAPI* api); }; /** * Renders tesseract output into a plain UTF-8 text string */ class TESS_API TessBoxTextRenderer : public TessResultRenderer { public: explicit TessBoxTextRenderer(const char *outputbase); protected: virtual bool AddImageHandler(TessBaseAPI* api); }; /** * Renders tesseract output into an osd text string */ class TESS_API TessOsdRenderer : public TessResultRenderer { public: explicit TessOsdRenderer(const char* outputbase); protected: virtual bool AddImageHandler(TessBaseAPI* api); }; } // namespace tesseract. #endif // TESSERACT_API_RENDERER_H__ tesseract-3.04.01/api/tesseractmain.cpp000066400000000000000000000342231266071204500200020ustar00rootroot00000000000000/********************************************************************** * File: tessedit.cpp (Formerly tessedit.c) * Description: Main program for merge of tess and editor. * Author: Ray Smith * Created: Tue Jan 07 15:21:46 GMT 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. * **********************************************************************/ // Include automatically generated configuration file if running autoconf #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include #include "allheaders.h" #include "baseapi.h" #include "basedir.h" #include "renderer.h" #include "strngs.h" #include "tprintf.h" #include "openclwrapper.h" #include "osdetect.h" void PrintVersionInfo() { char *versionStrP; fprintf(stderr, "tesseract %s\n", tesseract::TessBaseAPI::Version()); versionStrP = getLeptonicaVersion(); fprintf(stderr, " %s\n", versionStrP); lept_free(versionStrP); versionStrP = getImagelibVersions(); fprintf(stderr, " %s\n", versionStrP); lept_free(versionStrP); #ifdef USE_OPENCL cl_platform_id platform; cl_uint num_platforms; cl_device_id devices[2]; cl_uint num_devices; char info[256]; int i; fprintf(stderr, " OpenCL info:\n"); clGetPlatformIDs(1, &platform, &num_platforms); fprintf(stderr, " Found %d platforms.\n", num_platforms); clGetPlatformInfo(platform, CL_PLATFORM_NAME, 256, info, 0); fprintf(stderr, " Platform name: %s.\n", info); clGetPlatformInfo(platform, CL_PLATFORM_VERSION, 256, info, 0); fprintf(stderr, " Version: %s.\n", info); clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, 2, devices, &num_devices); fprintf(stderr, " Found %d devices.\n", num_devices); for (i = 0; i < num_devices; ++i) { clGetDeviceInfo(devices[i], CL_DEVICE_NAME, 256, info, 0); fprintf(stderr, " Device %d name: %s.\n", i+1, info); } #endif } void PrintUsage(const char* program) { fprintf(stderr, "Usage:\n" " %s --help | --help-psm | --version\n" " %s --list-langs [--tessdata-dir PATH]\n" " %s --print-parameters [options...] [configfile...]\n" " %s imagename|stdin outputbase|stdout [options...] [configfile...]\n", program, program, program, program); } void PrintHelpForPSM() { const char* msg = "Page segmentation modes:\n" " 0 Orientation and script detection (OSD) only.\n" " 1 Automatic page segmentation with OSD.\n" " 2 Automatic page segmentation, but no OSD, or OCR.\n" " 3 Fully automatic page segmentation, but no OSD. (Default)\n" " 4 Assume a single column of text of variable sizes.\n" " 5 Assume a single uniform block of vertically aligned text.\n" " 6 Assume a single uniform block of text.\n" " 7 Treat the image as a single text line.\n" " 8 Treat the image as a single word.\n" " 9 Treat the image as a single word in a circle.\n" " 10 Treat the image as a single character.\n" //TODO: Consider publishing these modes. #if 0 " 11 Sparse text. Find as much text as possible in no" " particular order.\n" " 12 Sparse text with OSD.\n" " 13 Raw line. Treat the image as a single text line,\n" "\t\t\tbypassing hacks that are Tesseract-specific.\n" #endif ; fprintf(stderr, "%s", msg); } void PrintHelpMessage(const char* program) { PrintUsage(program); const char* ocr_options = "OCR options:\n" " --tessdata-dir PATH Specify the location of tessdata path.\n" " --user-words PATH Specify the location of user words file.\n" " --user-patterns PATH Specify the location of user patterns file.\n" " -l LANG[+LANG] Specify language(s) used for OCR.\n" " -c VAR=VALUE Set value for config variables.\n" " Multiple -c arguments are allowed.\n" " -psm NUM Specify page segmentation mode.\n" "NOTE: These options must occur before any configfile.\n" ; fprintf(stderr, "\n%s\n", ocr_options); PrintHelpForPSM(); const char *single_options = "Single options:\n" " -h, --help Show this help message.\n" " --help-psm Show page segmentation modes.\n" " -v, --version Show version information.\n" " --list-langs List available languages for tesseract engine.\n" " --print-parameters Print tesseract parameters to stdout.\n" ; fprintf(stderr, "\n%s", single_options); } void SetVariablesFromCLArgs(tesseract::TessBaseAPI* api, int argc, char** argv) { char opt1[256], opt2[255]; for (int i = 0; i < argc; i++) { if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) { strncpy(opt1, argv[i + 1], 255); opt1[255] = '\0'; char *p = strchr(opt1, '='); if (!p) { fprintf(stderr, "Missing = in configvar assignment\n"); exit(1); } *p = 0; strncpy(opt2, strchr(argv[i + 1], '=') + 1, 255); opt2[254] = 0; ++i; if (!api->SetVariable(opt1, opt2)) { fprintf(stderr, "Could not set option: %s=%s\n", opt1, opt2); } } } } void PrintLangsList(tesseract::TessBaseAPI* api) { GenericVector languages; api->GetAvailableLanguagesAsVector(&languages); fprintf(stderr, "List of available languages (%d):\n", languages.size()); for (int index = 0; index < languages.size(); ++index) { STRING& string = languages[index]; fprintf(stderr, "%s\n", string.string()); } api->End(); } /** * We have 2 possible sources of pagesegmode: a config file and * the command line. For backwards compatibility reasons, the * default in tesseract is tesseract::PSM_SINGLE_BLOCK, but the * default for this program is tesseract::PSM_AUTO. We will let * the config file take priority, so the command-line default * can take priority over the tesseract default, so we use the * value from the command line only if the retrieved mode * is still tesseract::PSM_SINGLE_BLOCK, indicating no change * in any config file. Therefore the only way to force * tesseract::PSM_SINGLE_BLOCK is from the command line. * It would be simpler if we could set the value before Init, * but that doesn't work. */ void FixPageSegMode(tesseract::TessBaseAPI* api, tesseract::PageSegMode pagesegmode) { if (api->GetPageSegMode() == tesseract::PSM_SINGLE_BLOCK) api->SetPageSegMode(pagesegmode); } // NOTE: arg_i is used here to avoid ugly *i so many times in this function void ParseArgs(const int argc, char** argv, const char** lang, const char** image, const char** outputbase, const char** datapath, bool* list_langs, bool* print_parameters, GenericVector* vars_vec, GenericVector* vars_values, int* arg_i, tesseract::PageSegMode* pagesegmode) { if (argc == 1) { PrintHelpMessage(argv[0]); exit(0); } if (argc == 2) { if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) { PrintHelpMessage(argv[0]); exit(0); } if ((strcmp(argv[1], "--help-psm") == 0)) { PrintHelpForPSM(); exit(0); } if ((strcmp(argv[1], "-v") == 0) || (strcmp(argv[1], "--version") == 0)) { PrintVersionInfo(); exit(0); } } bool noocr = false; int i = 1; while (i < argc && (*outputbase == NULL || argv[i][0] == '-')) { if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) { *lang = argv[i + 1]; ++i; } else if (strcmp(argv[i], "--tessdata-dir") == 0 && i + 1 < argc) { *datapath = argv[i + 1]; ++i; } else if (strcmp(argv[i], "--user-words") == 0 && i + 1 < argc) { vars_vec->push_back("user_words_file"); vars_values->push_back(argv[i + 1]); ++i; } else if (strcmp(argv[i], "--user-patterns") == 0 && i + 1 < argc) { vars_vec->push_back("user_patterns_file"); vars_values->push_back(argv[i + 1]); ++i; } else if (strcmp(argv[i], "--list-langs") == 0) { noocr = true; *list_langs = true; } else if (strcmp(argv[i], "-psm") == 0 && i + 1 < argc) { *pagesegmode = static_cast(atoi(argv[i + 1])); ++i; } else if (strcmp(argv[i], "--print-parameters") == 0) { noocr = true; *print_parameters = true; } else if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) { // handled properly after api init ++i; } else if (*image == NULL) { *image = argv[i]; } else if (*outputbase == NULL) { *outputbase = argv[i]; } ++i; } *arg_i = i; if (argc == 2 && strcmp(argv[1], "--list-langs") == 0) { *list_langs = true; noocr = true; } if (*outputbase == NULL && noocr == false) { PrintHelpMessage(argv[0]); exit(1); } if (*outputbase != NULL && strcmp(*outputbase, "-") && strcmp(*outputbase, "stdout")) { tprintf("Tesseract Open Source OCR Engine v%s with Leptonica\n", tesseract::TessBaseAPI::Version()); } } void PreloadRenderers(tesseract::TessBaseAPI* api, tesseract::PointerVector* renderers, tesseract::PageSegMode pagesegmode, const char* outputbase) { if (pagesegmode == tesseract::PSM_OSD_ONLY) { renderers->push_back(new tesseract::TessOsdRenderer(outputbase)); } else { bool b; api->GetBoolVariable("tessedit_create_hocr", &b); if (b) { bool font_info; api->GetBoolVariable("hocr_font_info", &font_info); renderers->push_back( new tesseract::TessHOcrRenderer(outputbase, font_info)); } api->GetBoolVariable("tessedit_create_pdf", &b); if (b) { renderers->push_back(new tesseract::TessPDFRenderer(outputbase, api->GetDatapath())); } api->GetBoolVariable("tessedit_write_unlv", &b); if (b) { renderers->push_back(new tesseract::TessUnlvRenderer(outputbase)); } api->GetBoolVariable("tessedit_create_boxfile", &b); if (b) { renderers->push_back(new tesseract::TessBoxTextRenderer(outputbase)); } api->GetBoolVariable("tessedit_create_txt", &b); if (b || renderers->empty()) { renderers->push_back(new tesseract::TessTextRenderer(outputbase)); } } if (!renderers->empty()) { // Since the PointerVector auto-deletes, null-out the renderers that are // added to the root, and leave the root in the vector. for (int r = 1; r < renderers->size(); ++r) { (*renderers)[0]->insert((*renderers)[r]); (*renderers)[r] = NULL; } } } /********************************************************************** * main() * **********************************************************************/ int main(int argc, char **argv) { const char* lang = "eng"; const char* image = NULL; const char* outputbase = NULL; const char* datapath = NULL; bool list_langs = false; bool print_parameters = false; GenericVector vars_vec, vars_values; int arg_i = 1; tesseract::PageSegMode pagesegmode = tesseract::PSM_AUTO; ParseArgs(argc, argv, &lang, &image, &outputbase, &datapath, &list_langs, &print_parameters, &vars_vec, &vars_values, &arg_i, &pagesegmode); PERF_COUNT_START("Tesseract:main") tesseract::TessBaseAPI api; api.SetOutputName(outputbase); int init_failed = api.Init(datapath, lang, tesseract::OEM_DEFAULT, &(argv[arg_i]), argc - arg_i, &vars_vec, &vars_values, false); if (init_failed) { fprintf(stderr, "Could not initialize tesseract.\n"); exit(1); } SetVariablesFromCLArgs(&api, argc, argv); if (list_langs) { PrintLangsList(&api); exit(0); } if (print_parameters) { FILE* fout = stdout; fprintf(stdout, "Tesseract parameters:\n"); api.PrintVariables(fout); api.End(); exit(0); } FixPageSegMode(&api, pagesegmode); if (pagesegmode == tesseract::PSM_AUTO_ONLY) { int ret_val = 0; Pix* pixs = pixRead(image); if (!pixs) { fprintf(stderr, "Cannot open input file: %s\n", image); exit(2); } api.SetImage(pixs); tesseract::Orientation orientation; tesseract::WritingDirection direction; tesseract::TextlineOrder order; float deskew_angle; tesseract::PageIterator* it = api.AnalyseLayout(); if (it) { it->Orientation(&orientation, &direction, &order, &deskew_angle); tprintf("Orientation: %d\nWritingDirection: %d\nTextlineOrder: %d\n" \ "Deskew angle: %.4f\n", orientation, direction, order, deskew_angle); } else { ret_val = 1; } delete it; pixDestroy(&pixs); exit(ret_val); } // set in_training_mode to true when using one of these configs: // ambigs.train, box.train, box.train.stderr, linebox, rebox bool b = false; bool in_training_mode = (api.GetBoolVariable("tessedit_ambigs_training", &b) && b) || (api.GetBoolVariable("tessedit_resegment_from_boxes", &b) && b) || (api.GetBoolVariable("tessedit_make_boxes_from_boxes", &b) && b); tesseract::PointerVector renderers; if (in_training_mode) { renderers.push_back(NULL); } else { PreloadRenderers(&api, &renderers, pagesegmode, outputbase); } if (!renderers.empty()) { bool succeed = api.ProcessPages(image, NULL, 0, renderers[0]); if (!succeed) { fprintf(stderr, "Error during processing.\n"); exit(1); } } PERF_COUNT_END return 0; // Normal exit } tesseract-3.04.01/autogen.sh000077500000000000000000000052211266071204500156600ustar00rootroot00000000000000#!/bin/sh # This is a simple script which is meant to help developers # better deal with the GNU autotools, specifically: # # aclocal # autoheader # autoconf # automake # # The whole thing is quite complex... # # The idea is to run this collection of tools on a single platform, # typically the main development platform, running a recent version of # autoconf. In theory, if we had these tools on each platform where we # ever expected to port the software, we would never need to checkin # more than a few autotools configuration files. However, the whole # idea is to generate a configure script and associated files in a way # that is portable across platforms, so we *have* to check in a whole # bunch of files generated by all these tools. # The real source files are: # # acinclude.m4 (used by aclocal) # configure.ac (main autoconf file) # Makefile.am, */Makefile.am (automake config files) # # All the rest is auto-generated. if [ "$1" = "clean" ]; then echo "Cleaning..." rm configure aclocal.m4 rm m4/* rmdir m4 rm config/* rmdir config find . -iname "Makefile.in" -type f -exec rm '{}' + fi # create m4 directory if it not exists if [ ! -d m4 ]; then mkdir m4 fi bail_out() { echo echo " Something went wrong, bailing out!" echo exit 1 } # --- Step 1: Generate aclocal.m4 from: # . acinclude.m4 # . config/*.m4 (these files are referenced in acinclude.m4) mkdir -p config echo "Running aclocal" aclocal -I config || bail_out # --- Step 2: echo "Running libtoolize" libtoolize -f -c || glibtoolize -f -c || bail_out libtoolize --automake || glibtoolize --automake || bail_out # --- Step 3: Generate config.h.in from: # . configure.ac (look for AM_CONFIG_HEADER tag or AC_CONFIG_HEADER tag) echo "Running autoheader" autoheader -f || bail_out # --- Step 4: Generate Makefile.in, src/Makefile.in, and a whole bunch of # files in config (config.guess, config.sub, depcomp, # install-sh, missing, mkinstalldirs) plus COPYING and # INSTALL from: # . Makefile.am # . src/Makefile.am # # Using --add-missing --copy makes sure that, if these files are missing, # they are copied from the system so they can be used in a distribution. echo "Running automake --add-missing --copy" automake --add-missing -c -Wno-portability > /dev/null || bail_out # --- Step 5: Generate configure and include/miaconfig.h from: # . configure.ac # echo "Running autoconf" autoconf || bail_out echo "" echo "All done." echo "To build the software now, do something like:" echo "" echo "$ ./configure [--enable-debug] [...other options]" tesseract-3.04.01/ccmain/000077500000000000000000000000001266071204500151115ustar00rootroot00000000000000tesseract-3.04.01/ccmain/Makefile.am000066400000000000000000000043071266071204500171510ustar00rootroot00000000000000AM_CPPFLAGS += \ -DUSE_STD_NAMESPACE \ -I$(top_srcdir)/ccutil -I$(top_srcdir)/ccstruct \ -I$(top_srcdir)/viewer \ -I$(top_srcdir)/classify -I$(top_srcdir)/dict \ -I$(top_srcdir)/wordrec -I$(top_srcdir)/cutil \ -I$(top_srcdir)/textord -I$(top_srcdir)/opencl AM_CPPFLAGS += $(OPENCL_CPPFLAGS) if VISIBILITY AM_CPPFLAGS += -DTESS_EXPORTS \ -fvisibility=hidden -fvisibility-inlines-hidden endif include_HEADERS = \ thresholder.h ltrresultiterator.h pageiterator.h resultiterator.h \ osdetect.h noinst_HEADERS = \ control.h docqual.h equationdetect.h fixspace.h mutableiterator.h \ output.h paragraphs.h paragraphs_internal.h paramsd.h pgedit.h \ reject.h tessbox.h tessedit.h tesseractclass.h tessvars.h werdit.h if !USING_MULTIPLELIBS noinst_LTLIBRARIES = libtesseract_main.la else lib_LTLIBRARIES = libtesseract_main.la libtesseract_main_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) libtesseract_main_la_LIBADD = \ ../wordrec/libtesseract_wordrec.la \ ../textord/libtesseract_textord.la \ ../ccutil/libtesseract_ccutil.la \ ../ccstruct/libtesseract_ccstruct.la \ ../viewer/libtesseract_viewer.la \ ../dict/libtesseract_dict.la \ ../classify/libtesseract_classify.la \ ../cutil/libtesseract_cutil.la \ ../opencl/libtesseract_opencl.la if !NO_CUBE_BUILD libtesseract_main_la_LIBADD += ../cube/libtesseract_cube.la endif endif libtesseract_main_la_SOURCES = \ adaptions.cpp applybox.cpp control.cpp \ docqual.cpp equationdetect.cpp fixspace.cpp fixxht.cpp \ ltrresultiterator.cpp \ osdetect.cpp output.cpp pageiterator.cpp pagesegmain.cpp \ pagewalk.cpp par_control.cpp paragraphs.cpp paramsd.cpp pgedit.cpp recogtraining.cpp \ reject.cpp resultiterator.cpp superscript.cpp \ tessbox.cpp tessedit.cpp tesseractclass.cpp tessvars.cpp \ tfacepp.cpp thresholder.cpp \ werdit.cpp if !NO_CUBE_BUILD AM_CPPFLAGS += \ -I$(top_srcdir)/neural_networks/runtime -I$(top_srcdir)/cube noinst_HEADERS += \ cube_reco_context.h cubeclassifier.h tesseract_cube_combiner.h libtesseract_main_la_SOURCES += \ cube_control.cpp cube_reco_context.cpp cubeclassifier.cpp \ tesseract_cube_combiner.cpp endif tesseract-3.04.01/ccmain/Makefile.in000066400000000000000000000712541266071204500171670ustar00rootroot00000000000000# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @VISIBILITY_TRUE@am__append_1 = -DTESS_EXPORTS \ @VISIBILITY_TRUE@ -fvisibility=hidden -fvisibility-inlines-hidden @NO_CUBE_BUILD_FALSE@am__append_2 = \ @NO_CUBE_BUILD_FALSE@ -I$(top_srcdir)/neural_networks/runtime -I$(top_srcdir)/cube @NO_CUBE_BUILD_FALSE@am__append_3 = \ @NO_CUBE_BUILD_FALSE@ cube_reco_context.h cubeclassifier.h tesseract_cube_combiner.h @NO_CUBE_BUILD_FALSE@am__append_4 = \ @NO_CUBE_BUILD_FALSE@ cube_control.cpp cube_reco_context.cpp cubeclassifier.cpp \ @NO_CUBE_BUILD_FALSE@ tesseract_cube_combiner.cpp subdir = ccmain DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/config/depcomp $(include_HEADERS) \ $(am__noinst_HEADERS_DIST) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config_auto.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)" LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) @USING_MULTIPLELIBS_TRUE@libtesseract_main_la_DEPENDENCIES = \ @USING_MULTIPLELIBS_TRUE@ ../wordrec/libtesseract_wordrec.la \ @USING_MULTIPLELIBS_TRUE@ ../textord/libtesseract_textord.la \ @USING_MULTIPLELIBS_TRUE@ ../ccutil/libtesseract_ccutil.la \ @USING_MULTIPLELIBS_TRUE@ ../ccstruct/libtesseract_ccstruct.la \ @USING_MULTIPLELIBS_TRUE@ ../viewer/libtesseract_viewer.la \ @USING_MULTIPLELIBS_TRUE@ ../dict/libtesseract_dict.la \ @USING_MULTIPLELIBS_TRUE@ ../classify/libtesseract_classify.la \ @USING_MULTIPLELIBS_TRUE@ ../cutil/libtesseract_cutil.la \ @USING_MULTIPLELIBS_TRUE@ ../opencl/libtesseract_opencl.la \ @USING_MULTIPLELIBS_TRUE@ ../cube/libtesseract_cube.la am__libtesseract_main_la_SOURCES_DIST = adaptions.cpp applybox.cpp \ control.cpp docqual.cpp equationdetect.cpp fixspace.cpp \ fixxht.cpp ltrresultiterator.cpp osdetect.cpp output.cpp \ pageiterator.cpp pagesegmain.cpp pagewalk.cpp par_control.cpp \ paragraphs.cpp paramsd.cpp pgedit.cpp recogtraining.cpp \ reject.cpp resultiterator.cpp superscript.cpp tessbox.cpp \ tessedit.cpp tesseractclass.cpp tessvars.cpp tfacepp.cpp \ thresholder.cpp werdit.cpp cube_control.cpp \ cube_reco_context.cpp cubeclassifier.cpp \ tesseract_cube_combiner.cpp @NO_CUBE_BUILD_FALSE@am__objects_1 = cube_control.lo \ @NO_CUBE_BUILD_FALSE@ cube_reco_context.lo cubeclassifier.lo \ @NO_CUBE_BUILD_FALSE@ tesseract_cube_combiner.lo am_libtesseract_main_la_OBJECTS = adaptions.lo applybox.lo control.lo \ docqual.lo equationdetect.lo fixspace.lo fixxht.lo \ ltrresultiterator.lo osdetect.lo output.lo pageiterator.lo \ pagesegmain.lo pagewalk.lo par_control.lo paragraphs.lo \ paramsd.lo pgedit.lo recogtraining.lo reject.lo \ resultiterator.lo superscript.lo tessbox.lo tessedit.lo \ tesseractclass.lo tessvars.lo tfacepp.lo thresholder.lo \ werdit.lo $(am__objects_1) libtesseract_main_la_OBJECTS = $(am_libtesseract_main_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libtesseract_main_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ $(AM_CXXFLAGS) $(CXXFLAGS) $(libtesseract_main_la_LDFLAGS) \ $(LDFLAGS) -o $@ @USING_MULTIPLELIBS_FALSE@am_libtesseract_main_la_rpath = @USING_MULTIPLELIBS_TRUE@am_libtesseract_main_la_rpath = -rpath \ @USING_MULTIPLELIBS_TRUE@ $(libdir) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__depfiles_maybe = depfiles am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(libtesseract_main_la_SOURCES) DIST_SOURCES = $(am__libtesseract_main_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__noinst_HEADERS_DIST = control.h docqual.h equationdetect.h \ fixspace.h mutableiterator.h output.h paragraphs.h \ paragraphs_internal.h paramsd.h pgedit.h reject.h tessbox.h \ tessedit.h tesseractclass.h tessvars.h werdit.h \ cube_reco_context.h cubeclassifier.h tesseract_cube_combiner.h HEADERS = $(include_HEADERS) $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_CPPFLAGS = @AM_CPPFLAGS@ -DUSE_STD_NAMESPACE -I$(top_srcdir)/ccutil \ -I$(top_srcdir)/ccstruct -I$(top_srcdir)/viewer \ -I$(top_srcdir)/classify -I$(top_srcdir)/dict \ -I$(top_srcdir)/wordrec -I$(top_srcdir)/cutil \ -I$(top_srcdir)/textord -I$(top_srcdir)/opencl \ $(OPENCL_CPPFLAGS) $(am__append_1) $(am__append_2) AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AM_LDFLAGS = @AM_LDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FRAMEWORK_OPENCL = @FRAMEWORK_OPENCL@ GENERIC_API_VERSION = @GENERIC_API_VERSION@ GENERIC_LIBRARY_NAME = @GENERIC_LIBRARY_NAME@ GENERIC_LIBRARY_VERSION = @GENERIC_LIBRARY_VERSION@ GENERIC_MAJOR_VERSION = @GENERIC_MAJOR_VERSION@ GENERIC_RELEASE = @GENERIC_RELEASE@ GENERIC_VERSION = @GENERIC_VERSION@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBLEPT_HEADERSDIR = @LIBLEPT_HEADERSDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCL_CPPFLAGS = @OPENCL_CPPFLAGS@ OPENCL_LDFLAGS = @OPENCL_LDFLAGS@ OPENMP_CXXFLAGS = @OPENMP_CXXFLAGS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_DATE = @PACKAGE_DATE@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_YEAR = @PACKAGE_YEAR@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ include_HEADERS = \ thresholder.h ltrresultiterator.h pageiterator.h resultiterator.h \ osdetect.h noinst_HEADERS = control.h docqual.h equationdetect.h fixspace.h \ mutableiterator.h output.h paragraphs.h paragraphs_internal.h \ paramsd.h pgedit.h reject.h tessbox.h tessedit.h \ tesseractclass.h tessvars.h werdit.h $(am__append_3) @USING_MULTIPLELIBS_FALSE@noinst_LTLIBRARIES = libtesseract_main.la @USING_MULTIPLELIBS_TRUE@lib_LTLIBRARIES = libtesseract_main.la @USING_MULTIPLELIBS_TRUE@libtesseract_main_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) @USING_MULTIPLELIBS_TRUE@libtesseract_main_la_LIBADD = \ @USING_MULTIPLELIBS_TRUE@ ../wordrec/libtesseract_wordrec.la \ @USING_MULTIPLELIBS_TRUE@ ../textord/libtesseract_textord.la \ @USING_MULTIPLELIBS_TRUE@ ../ccutil/libtesseract_ccutil.la \ @USING_MULTIPLELIBS_TRUE@ ../ccstruct/libtesseract_ccstruct.la \ @USING_MULTIPLELIBS_TRUE@ ../viewer/libtesseract_viewer.la \ @USING_MULTIPLELIBS_TRUE@ ../dict/libtesseract_dict.la \ @USING_MULTIPLELIBS_TRUE@ ../classify/libtesseract_classify.la \ @USING_MULTIPLELIBS_TRUE@ ../cutil/libtesseract_cutil.la \ @USING_MULTIPLELIBS_TRUE@ ../opencl/libtesseract_opencl.la \ @USING_MULTIPLELIBS_TRUE@ ../cube/libtesseract_cube.la libtesseract_main_la_SOURCES = adaptions.cpp applybox.cpp control.cpp \ docqual.cpp equationdetect.cpp fixspace.cpp fixxht.cpp \ ltrresultiterator.cpp osdetect.cpp output.cpp pageiterator.cpp \ pagesegmain.cpp pagewalk.cpp par_control.cpp paragraphs.cpp \ paramsd.cpp pgedit.cpp recogtraining.cpp reject.cpp \ resultiterator.cpp superscript.cpp tessbox.cpp tessedit.cpp \ tesseractclass.cpp tessvars.cpp tfacepp.cpp thresholder.cpp \ werdit.cpp $(am__append_4) all: all-am .SUFFIXES: .SUFFIXES: .cpp .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign ccmain/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign ccmain/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libtesseract_main.la: $(libtesseract_main_la_OBJECTS) $(libtesseract_main_la_DEPENDENCIES) $(EXTRA_libtesseract_main_la_DEPENDENCIES) $(AM_V_CXXLD)$(libtesseract_main_la_LINK) $(am_libtesseract_main_la_rpath) $(libtesseract_main_la_OBJECTS) $(libtesseract_main_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adaptions.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/applybox.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cube_control.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cube_reco_context.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cubeclassifier.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/docqual.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/equationdetect.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fixspace.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fixxht.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ltrresultiterator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/osdetect.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pageiterator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pagesegmain.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pagewalk.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/par_control.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/paragraphs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/paramsd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pgedit.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/recogtraining.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reject.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resultiterator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/superscript.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tessbox.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tessedit.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tesseract_cube_combiner.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tesseractclass.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tessvars.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tfacepp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thresholder.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/werdit.Plo@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-includeHEADERS: $(include_HEADERS) @$(NORMAL_INSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ done uninstall-includeHEADERS: @$(NORMAL_UNINSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ clean-noinstLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-includeHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-libLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libLTLIBRARIES clean-libtool clean-noinstLTLIBRARIES \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-includeHEADERS install-info install-info-am \ install-libLTLIBRARIES install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-includeHEADERS \ uninstall-libLTLIBRARIES @USING_MULTIPLELIBS_TRUE@ if !NO_CUBE_BUILD @USING_MULTIPLELIBS_TRUE@ endif # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: tesseract-3.04.01/ccmain/adaptions.cpp000066400000000000000000000074331266071204500176060ustar00rootroot00000000000000/********************************************************************** * File: adaptions.cpp (Formerly adaptions.c) * Description: Functions used to adapt to blobs already confidently * identified * Author: Chris Newton * Created: Thu Oct 7 10:17:28 BST 1993 * * (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. * **********************************************************************/ #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #pragma warning(disable:4305) // int/float warnings #endif #ifdef __UNIX__ #include #endif #include #include #include "tessbox.h" #include "tessvars.h" #include "memry.h" #include "reject.h" #include "control.h" #include "stopper.h" #include "tesseractclass.h" // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif namespace tesseract { BOOL8 Tesseract::word_adaptable( //should we adapt? WERD_RES *word, uinT16 mode) { if (tessedit_adaption_debug) { tprintf("Running word_adaptable() for %s rating %.4f certainty %.4f\n", word->best_choice == NULL ? "" : word->best_choice->unichar_string().string(), word->best_choice->rating(), word->best_choice->certainty()); } BOOL8 status = FALSE; BITS16 flags(mode); enum MODES { ADAPTABLE_WERD, ACCEPTABLE_WERD, CHECK_DAWGS, CHECK_SPACES, CHECK_ONE_ELL_CONFLICT, CHECK_AMBIG_WERD }; /* 0: NO adaption */ if (mode == 0) { if (tessedit_adaption_debug) tprintf("adaption disabled\n"); return FALSE; } if (flags.bit (ADAPTABLE_WERD)) { status |= word->tess_would_adapt; // result of Classify::AdaptableWord() if (tessedit_adaption_debug && !status) { tprintf("tess_would_adapt bit is false\n"); } } if (flags.bit (ACCEPTABLE_WERD)) { status |= word->tess_accepted; if (tessedit_adaption_debug && !status) { tprintf("tess_accepted bit is false\n"); } } if (!status) { // If not set then return FALSE; // ignore other checks } if (flags.bit (CHECK_DAWGS) && (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)) { if (tessedit_adaption_debug) tprintf("word not in dawgs\n"); return FALSE; } if (flags.bit (CHECK_ONE_ELL_CONFLICT) && one_ell_conflict (word, FALSE)) { if (tessedit_adaption_debug) tprintf("word has ell conflict\n"); return FALSE; } if (flags.bit (CHECK_SPACES) && (strchr(word->best_choice->unichar_string().string(), ' ') != NULL)) { if (tessedit_adaption_debug) tprintf("word contains spaces\n"); return FALSE; } if (flags.bit (CHECK_AMBIG_WERD) && word->best_choice->dangerous_ambig_found()) { if (tessedit_adaption_debug) tprintf("word is ambiguous\n"); return FALSE; } if (tessedit_adaption_debug) { tprintf("returning status %d\n", status); } return status; } } // namespace tesseract tesseract-3.04.01/ccmain/applybox.cpp000066400000000000000000001012321266071204500174520ustar00rootroot00000000000000/********************************************************************** * File: applybox.cpp (Formerly applybox.c) * Description: Re segment rows according to box file data * Author: Phil Cheatle * Created: Wed Nov 24 09:11:23 GMT 1993 * * (C) Copyright 1993, 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. * **********************************************************************/ #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #endif #include #include #ifdef __UNIX__ #include #include #endif #include "allheaders.h" #include "boxread.h" #include "chopper.h" #include "pageres.h" #include "unichar.h" #include "unicharset.h" #include "tesseractclass.h" #include "genericvector.h" /** Max number of blobs to classify together in FindSegmentation. */ const int kMaxGroupSize = 4; /// Max fraction of median allowed as deviation in xheight before switching /// to median. const double kMaxXHeightDeviationFraction = 0.125; /** * The box file is assumed to contain box definitions, one per line, of the * following format for blob-level boxes: * @verbatim * * @endverbatim * and for word/line-level boxes: * @verbatim * WordStr # * @endverbatim * NOTES: * The boxes use tesseract coordinates, i.e. 0,0 is at BOTTOM-LEFT. * * is 0-based, and the page number is used for multipage input (tiff). * * In the blob-level form, each line represents a recognizable unit, which may * be several UTF-8 bytes, but there is a bounding box around each recognizable * unit, and no classifier is needed to train in this mode (bootstrapping.) * * In the word/line-level form, the line begins with the literal "WordStr", and * the bounding box bounds either a whole line or a whole word. The recognizable * units in the word/line are listed after the # at the end of the line and * are space delimited, ignoring any original spaces on the line. * Eg. * @verbatim * word -> #w o r d * multi word line -> #m u l t i w o r d l i n e * @endverbatim * The recognizable units must be space-delimited in order to allow multiple * unicodes to be used for a single recognizable unit, eg Hindi. * * In this mode, the classifier must have been pre-trained with the desired * character set, or it will not be able to find the character segmentations. */ namespace tesseract { static void clear_any_old_text(BLOCK_LIST *block_list) { BLOCK_IT block_it(block_list); for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) { ROW_IT row_it(block_it.data()->row_list()); for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { WERD_IT word_it(row_it.data()->word_list()); for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) { word_it.data()->set_text(""); } } } } // Applies the box file based on the image name fname, and resegments // the words in the block_list (page), with: // blob-mode: one blob per line in the box file, words as input. // word/line-mode: one blob per space-delimited unit after the #, and one word // per line in the box file. (See comment above for box file format.) // If find_segmentation is true, (word/line mode) then the classifier is used // to re-segment words/lines to match the space-delimited truth string for // each box. In this case, the input box may be for a word or even a whole // text line, and the output words will contain multiple blobs corresponding // to the space-delimited input string. // With find_segmentation false, no classifier is needed, but the chopper // can still be used to correctly segment touching characters with the help // of the input boxes. // In the returned PAGE_RES, the WERD_RES are setup as they would be returned // from normal classification, ie. with a word, chopped_word, rebuild_word, // seam_array, denorm, box_word, and best_state, but NO best_choice or // raw_choice, as they would require a UNICHARSET, which we aim to avoid. // Instead, the correct_text member of WERD_RES is set, and this may be later // converted to a best_choice using CorrectClassifyWords. CorrectClassifyWords // is not required before calling ApplyBoxTraining. PAGE_RES* Tesseract::ApplyBoxes(const STRING& fname, bool find_segmentation, BLOCK_LIST *block_list) { GenericVector boxes; GenericVector texts, full_texts; if (!ReadAllBoxes(applybox_page, true, fname, &boxes, &texts, &full_texts, NULL)) { return NULL; // Can't do it. } int box_count = boxes.size(); int box_failures = 0; // Add an empty everything to the end. boxes.push_back(TBOX()); texts.push_back(STRING()); full_texts.push_back(STRING()); // In word mode, we use the boxes to make a word for each box, but // in blob mode we use the existing words and maximally chop them first. PAGE_RES* page_res = find_segmentation ? NULL : SetupApplyBoxes(boxes, block_list); clear_any_old_text(block_list); for (int i = 0; i < boxes.size() - 1; i++) { bool foundit = false; if (page_res != NULL) { if (i == 0) { foundit = ResegmentCharBox(page_res, NULL, boxes[i], boxes[i + 1], full_texts[i].string()); } else { foundit = ResegmentCharBox(page_res, &boxes[i-1], boxes[i], boxes[i + 1], full_texts[i].string()); } } else { foundit = ResegmentWordBox(block_list, boxes[i], boxes[i + 1], texts[i].string()); } if (!foundit) { box_failures++; ReportFailedBox(i, boxes[i], texts[i].string(), "FAILURE! Couldn't find a matching blob"); } } if (page_res == NULL) { // In word/line mode, we now maximally chop all the words and resegment // them with the classifier. page_res = SetupApplyBoxes(boxes, block_list); ReSegmentByClassification(page_res); } if (applybox_debug > 0) { tprintf("APPLY_BOXES:\n"); tprintf(" Boxes read from boxfile: %6d\n", box_count); if (box_failures > 0) tprintf(" Boxes failed resegmentation: %6d\n", box_failures); } TidyUp(page_res); return page_res; } // Helper computes median xheight in the image. static double MedianXHeight(BLOCK_LIST *block_list) { BLOCK_IT block_it(block_list); STATS xheights(0, block_it.data()->bounding_box().height()); for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) { ROW_IT row_it(block_it.data()->row_list()); for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { xheights.add(IntCastRounded(row_it.data()->x_height()), 1); } } return xheights.median(); } /// Any row xheight that is significantly different from the median is set /// to the median. void Tesseract::PreenXHeights(BLOCK_LIST *block_list) { double median_xheight = MedianXHeight(block_list); double max_deviation = kMaxXHeightDeviationFraction * median_xheight; // Strip all fuzzy space markers to simplify the PAGE_RES. BLOCK_IT b_it(block_list); for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { BLOCK* block = b_it.data(); ROW_IT r_it(block->row_list()); for (r_it.mark_cycle_pt(); !r_it.cycled_list(); r_it.forward ()) { ROW* row = r_it.data(); float diff = fabs(row->x_height() - median_xheight); if (diff > max_deviation) { if (applybox_debug) { tprintf("row xheight=%g, but median xheight = %g\n", row->x_height(), median_xheight); } row->set_x_height(static_cast(median_xheight)); } } } } /// Builds a PAGE_RES from the block_list in the way required for ApplyBoxes: /// All fuzzy spaces are removed, and all the words are maximally chopped. PAGE_RES* Tesseract::SetupApplyBoxes(const GenericVector& boxes, BLOCK_LIST *block_list) { PreenXHeights(block_list); // Strip all fuzzy space markers to simplify the PAGE_RES. BLOCK_IT b_it(block_list); for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { BLOCK* block = b_it.data(); ROW_IT r_it(block->row_list()); for (r_it.mark_cycle_pt(); !r_it.cycled_list(); r_it.forward ()) { ROW* row = r_it.data(); WERD_IT w_it(row->word_list()); for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) { WERD* word = w_it.data(); if (word->cblob_list()->empty()) { delete w_it.extract(); } else { word->set_flag(W_FUZZY_SP, false); word->set_flag(W_FUZZY_NON, false); } } } } PAGE_RES* page_res = new PAGE_RES(false, block_list, NULL); PAGE_RES_IT pr_it(page_res); WERD_RES* word_res; while ((word_res = pr_it.word()) != NULL) { MaximallyChopWord(boxes, pr_it.block()->block, pr_it.row()->row, word_res); pr_it.forward(); } return page_res; } /// 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. void Tesseract::MaximallyChopWord(const GenericVector& boxes, BLOCK* block, ROW* row, WERD_RES* word_res) { if (!word_res->SetupForRecognition(unicharset, this, BestPix(), tessedit_ocr_engine_mode, NULL, classify_bln_numeric_mode, textord_use_cjk_fp_model, poly_allow_detailed_fx, row, block)) { word_res->CloneChoppedToRebuild(); return; } if (chop_debug) { tprintf("Maximally chopping word at:"); word_res->word->bounding_box().print(); } GenericVector blob_choices; ASSERT_HOST(!word_res->chopped_word->blobs.empty()); float rating = static_cast(MAX_INT8); 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 // in here, and then divide by e each time they are chopped, which // should guarantee a set of unequal values for the whole tree of blobs // 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. BLOB_CHOICE* choice = new BLOB_CHOICE(0, rating, -rating, -1, 0.0f, 0.0f, 0.0f, BCC_FAKE); blob_choices.push_back(choice); rating -= 0.125f; } 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. 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, 0.0f, 0.0f, 0.0f, BCC_FAKE); blob_choices.insert(right_choice, blob_number + 1); } } word_res->CloneChoppedToRebuild(); word_res->FakeClassifyWord(blob_choices.size(), &blob_choices[0]); } /// Helper to compute the dispute resolution metric. /// Disputed blob resolution. The aim is to give the blob to the most /// appropriate boxfile box. Most of the time it is obvious, but if /// two boxfile boxes overlap significantly it is not. If a small boxfile /// box takes most of the blob, and a large boxfile box does too, then /// we want the small boxfile box to get it, but if the small box /// is much smaller than the blob, we don't want it to get it. /// Details of the disputed blob resolution: /// Given a box with area A, and a blob with area B, with overlap area C, /// then the miss metric is (A-C)(B-C)/(AB) and the box with minimum /// miss metric gets the blob. static double BoxMissMetric(const TBOX& box1, const TBOX& box2) { int overlap_area = box1.intersection(box2).area(); double miss_metric = box1.area()- overlap_area; miss_metric /= box1.area(); miss_metric *= box2.area() - overlap_area; miss_metric /= box2.area(); return miss_metric; } /// Gather consecutive blobs that match the given box into the best_state /// and corresponding correct_text. /// /// Fights over which box owns which blobs are settled by pre-chopping and /// applying the blobs to box or next_box with the least non-overlap. /// @return false if the box was in error, which can only be caused by /// failing to find an appropriate blob for a box. /// /// This means that occasionally, blobs may be incorrectly segmented if the /// chopper fails to find a suitable chop point. bool Tesseract::ResegmentCharBox(PAGE_RES* page_res, const TBOX *prev_box, const TBOX& box, const TBOX& next_box, const char* correct_text) { if (applybox_debug > 1) { tprintf("\nAPPLY_BOX: in ResegmentCharBox() for %s\n", correct_text); } PAGE_RES_IT page_res_it(page_res); WERD_RES* word_res; for (word_res = page_res_it.word(); word_res != NULL; word_res = page_res_it.forward()) { if (!word_res->box_word->bounding_box().major_overlap(box)) continue; if (applybox_debug > 1) { tprintf("Checking word box:"); word_res->box_word->bounding_box().print(); } int word_len = word_res->box_word->length(); for (int i = 0; i < word_len; ++i) { TBOX char_box = TBOX(); int blob_count = 0; for (blob_count = 0; i + blob_count < word_len; ++blob_count) { TBOX blob_box = word_res->box_word->BlobBox(i + blob_count); if (!blob_box.major_overlap(box)) break; if (word_res->correct_text[i + blob_count].length() > 0) break; // Blob is claimed already. double current_box_miss_metric = BoxMissMetric(blob_box, box); double next_box_miss_metric = BoxMissMetric(blob_box, next_box); if (applybox_debug > 2) { tprintf("Checking blob:"); blob_box.print(); tprintf("Current miss metric = %g, next = %g\n", current_box_miss_metric, next_box_miss_metric); } if (current_box_miss_metric > next_box_miss_metric) break; // Blob is a better match for next box. char_box += blob_box; } if (blob_count > 0) { if (applybox_debug > 1) { tprintf("Index [%d, %d) seem good.\n", i, i + blob_count); } if (!char_box.almost_equal(box, 3) && (box.x_gap(next_box) < -3 || (prev_box != NULL && prev_box->x_gap(box) < -3))) { return false; } // We refine just the box_word, best_state and correct_text here. // The rebuild_word is made in TidyUp. // blob_count blobs are put together to match the box. Merge the // box_word boxes, save the blob_count in the state and the text. word_res->box_word->MergeBoxes(i, i + blob_count); word_res->best_state[i] = blob_count; word_res->correct_text[i] = correct_text; if (applybox_debug > 2) { tprintf("%d Blobs match: blob box:", blob_count); word_res->box_word->BlobBox(i).print(); tprintf("Matches box:"); box.print(); tprintf("With next box:"); next_box.print(); } // Eliminated best_state and correct_text entries for the consumed // blobs. for (int j = 1; j < blob_count; ++j) { word_res->best_state.remove(i + 1); word_res->correct_text.remove(i + 1); } // Assume that no box spans multiple source words, so we are done with // this box. if (applybox_debug > 1) { tprintf("Best state = "); for (int j = 0; j < word_res->best_state.size(); ++j) { tprintf("%d ", word_res->best_state[j]); } tprintf("\n"); tprintf("Correct text = [[ "); for (int j = 0; j < word_res->correct_text.size(); ++j) { tprintf("%s ", word_res->correct_text[j].string()); } tprintf("]]\n"); } return true; } } } if (applybox_debug > 0) { tprintf("FAIL!\n"); } return false; // Failure. } /// Consume all source blobs that strongly overlap the given box, /// putting them into a new word, with the correct_text label. /// Fights over which box owns which blobs are settled by /// applying the blobs to box or next_box with the least non-overlap. /// @return false if the box was in error, which can only be caused by /// failing to find an overlapping blob for a box. bool Tesseract::ResegmentWordBox(BLOCK_LIST *block_list, const TBOX& box, const TBOX& next_box, const char* correct_text) { if (applybox_debug > 1) { tprintf("\nAPPLY_BOX: in ResegmentWordBox() for %s\n", correct_text); } WERD* new_word = NULL; BLOCK_IT b_it(block_list); for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { BLOCK* block = b_it.data(); if (!box.major_overlap(block->bounding_box())) continue; ROW_IT r_it(block->row_list()); for (r_it.mark_cycle_pt(); !r_it.cycled_list(); r_it.forward()) { ROW* row = r_it.data(); if (!box.major_overlap(row->bounding_box())) continue; WERD_IT w_it(row->word_list()); for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) { WERD* word = w_it.data(); if (applybox_debug > 2) { tprintf("Checking word:"); word->bounding_box().print(); } if (word->text() != NULL && word->text()[0] != '\0') continue; // Ignore words that are already done. if (!box.major_overlap(word->bounding_box())) continue; C_BLOB_IT blob_it(word->cblob_list()); for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { C_BLOB* blob = blob_it.data(); TBOX blob_box = blob->bounding_box(); if (!blob_box.major_overlap(box)) continue; double current_box_miss_metric = BoxMissMetric(blob_box, box); double next_box_miss_metric = BoxMissMetric(blob_box, next_box); if (applybox_debug > 2) { tprintf("Checking blob:"); blob_box.print(); tprintf("Current miss metric = %g, next = %g\n", current_box_miss_metric, next_box_miss_metric); } if (current_box_miss_metric > next_box_miss_metric) continue; // Blob is a better match for next box. if (applybox_debug > 2) { tprintf("Blob match: blob:"); blob_box.print(); tprintf("Matches box:"); box.print(); tprintf("With next box:"); next_box.print(); } if (new_word == NULL) { // Make a new word with a single blob. new_word = word->shallow_copy(); new_word->set_text(correct_text); w_it.add_to_end(new_word); } C_BLOB_IT new_blob_it(new_word->cblob_list()); new_blob_it.add_to_end(blob_it.extract()); } } } } if (new_word == NULL && applybox_debug > 0) tprintf("FAIL!\n"); return new_word != NULL; } /// Resegments the words by running the classifier in an attempt to find the /// correct segmentation that produces the required string. void Tesseract::ReSegmentByClassification(PAGE_RES* page_res) { PAGE_RES_IT pr_it(page_res); WERD_RES* word_res; for (; (word_res = pr_it.word()) != NULL; pr_it.forward()) { WERD* word = word_res->word; if (word->text() == NULL || word->text()[0] == '\0') continue; // Ignore words that have no text. // Convert the correct text to a vector of UNICHAR_ID GenericVector target_text; if (!ConvertStringToUnichars(word->text(), &target_text)) { tprintf("APPLY_BOX: FAILURE: can't find class_id for '%s'\n", word->text()); pr_it.DeleteCurrentWord(); continue; } if (!FindSegmentation(target_text, word_res)) { tprintf("APPLY_BOX: FAILURE: can't find segmentation for '%s'\n", word->text()); pr_it.DeleteCurrentWord(); continue; } } } /// Converts the space-delimited string of utf8 text to a vector of UNICHAR_ID. /// @return false if an invalid UNICHAR_ID is encountered. bool Tesseract::ConvertStringToUnichars(const char* utf8, GenericVector* class_ids) { for (int step = 0; *utf8 != '\0'; utf8 += step) { const char* next_space = strchr(utf8, ' '); if (next_space == NULL) next_space = utf8 + strlen(utf8); step = next_space - utf8; UNICHAR_ID class_id = unicharset.unichar_to_id(utf8, step); if (class_id == INVALID_UNICHAR_ID) { return false; } while (utf8[step] == ' ') ++step; class_ids->push_back(class_id); } return true; } /// Resegments the word to achieve the target_text from the classifier. /// Returns false if the re-segmentation fails. /// Uses brute-force combination of up to #kMaxGroupSize adjacent blobs, and /// applies a full search on the classifier results to find the best classified /// segmentation. As a compromise to obtain better recall, 1-1 ambiguity /// substitutions ARE used. bool Tesseract::FindSegmentation(const GenericVector& target_text, WERD_RES* word_res) { // Classify all required combinations of blobs and save results in choices. int word_length = word_res->box_word->length(); GenericVector* choices = new GenericVector[word_length]; 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->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); } choices[i].push_back(match_result); } } // Search the segmentation graph for the target text. Must be an exact // match. Using wildcards makes it difficult to find the correct // segmentation even when it is there. word_res->best_state.clear(); GenericVector search_segmentation; float best_rating = 0.0f; SearchForText(choices, 0, word_length, target_text, 0, 0.0f, &search_segmentation, &best_rating, &word_res->best_state); for (int i = 0; i < word_length; ++i) choices[i].delete_data_pointers(); delete [] choices; if (word_res->best_state.empty()) { // 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 < word_res->seam_array.size(); ++s) { SEAM* seam = word_res->seam_array[s]; if (!seam->HasAnySplits()) { word_res->best_state.push_back(blob_count); blob_count = 1; } else { ++blob_count; } } word_res->best_state.push_back(blob_count); if (word_res->best_state.size() != target_text.size()) { word_res->best_state.clear(); // No good. Original segmentation bad size. return false; } } word_res->correct_text.clear(); for (int i = 0; i < target_text.size(); ++i) { word_res->correct_text.push_back( STRING(unicharset.id_to_unichar(target_text[i]))); } return true; } /// Recursive helper to find a match to the target_text (from text_index /// position) in the choices (from choices_pos position). /// @param choices is an array of GenericVectors, of length choices_length, /// with each element representing a starting position in the word, and the /// #GenericVector holding classification results for a sequence of consecutive /// blobs, with index 0 being a single blob, index 1 being 2 blobs etc. /// @param choices_pos /// @param choices_length /// @param target_text /// @param text_index /// @param rating /// @param segmentation /// @param best_rating /// @param best_segmentation void Tesseract::SearchForText(const GenericVector* choices, int choices_pos, int choices_length, const GenericVector& target_text, int text_index, float rating, GenericVector* segmentation, float* best_rating, GenericVector* best_segmentation) { const UnicharAmbigsVector& table = getDict().getUnicharAmbigs().dang_ambigs(); for (int length = 1; length <= choices[choices_pos].size(); ++length) { // Rating of matching choice or worst choice if no match. float choice_rating = 0.0f; // Find the corresponding best BLOB_CHOICE. BLOB_CHOICE_IT choice_it(choices[choices_pos][length - 1]); for (choice_it.mark_cycle_pt(); !choice_it.cycled_list(); choice_it.forward()) { BLOB_CHOICE* choice = choice_it.data(); choice_rating = choice->rating(); UNICHAR_ID class_id = choice->unichar_id(); if (class_id == target_text[text_index]) { break; } // Search ambigs table. if (class_id < table.size() && table[class_id] != NULL) { AmbigSpec_IT spec_it(table[class_id]); for (spec_it.mark_cycle_pt(); !spec_it.cycled_list(); spec_it.forward()) { const AmbigSpec *ambig_spec = spec_it.data(); // We'll only do 1-1. if (ambig_spec->wrong_ngram[1] == INVALID_UNICHAR_ID && ambig_spec->correct_ngram_id == target_text[text_index]) break; } if (!spec_it.cycled_list()) break; // Found an ambig. } } if (choice_it.cycled_list()) continue; // No match. segmentation->push_back(length); if (choices_pos + length == choices_length && text_index + 1 == target_text.size()) { // This is a complete match. If the rating is good record a new best. if (applybox_debug > 2) { tprintf("Complete match, rating = %g, best=%g, seglength=%d, best=%d\n", rating + choice_rating, *best_rating, segmentation->size(), best_segmentation->size()); } if (best_segmentation->empty() || rating + choice_rating < *best_rating) { *best_segmentation = *segmentation; *best_rating = rating + choice_rating; } } else if (choices_pos + length < choices_length && text_index + 1 < target_text.size()) { if (applybox_debug > 3) { tprintf("Match found for %d=%s:%s, at %d+%d, recursing...\n", target_text[text_index], unicharset.id_to_unichar(target_text[text_index]), choice_it.data()->unichar_id() == target_text[text_index] ? "Match" : "Ambig", choices_pos, length); } SearchForText(choices, choices_pos + length, choices_length, target_text, text_index + 1, rating + choice_rating, segmentation, best_rating, best_segmentation); if (applybox_debug > 3) { tprintf("End recursion for %d=%s\n", target_text[text_index], unicharset.id_to_unichar(target_text[text_index])); } } segmentation->truncate(segmentation->size() - 1); } } /// - Counts up the labelled words and the blobs within. /// - Deletes all unused or emptied words, counting the unused ones. /// - Resets W_BOL and W_EOL flags correctly. /// - Builds the rebuild_word and rebuilds the box_word and the best_choice. void Tesseract::TidyUp(PAGE_RES* page_res) { int ok_blob_count = 0; int bad_blob_count = 0; int ok_word_count = 0; int unlabelled_words = 0; PAGE_RES_IT pr_it(page_res); WERD_RES* word_res; for (; (word_res = pr_it.word()) != NULL; pr_it.forward()) { int ok_in_word = 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. 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; word_res->LogNewRawChoice(word_choice); word_res->LogNewCookedChoice(1, false, word_choice); } else { ++unlabelled_words; if (applybox_debug > 0) { tprintf("APPLY_BOXES: Unlabelled word at :"); word_res->word->bounding_box().print(); } pr_it.DeleteCurrentWord(); delete word_choice; } } pr_it.restart_page(); for (; (word_res = pr_it.word()) != NULL; pr_it.forward()) { // Denormalize back to a BoxWord. word_res->RebuildBestState(); word_res->SetupBoxWord(); word_res->word->set_flag(W_BOL, pr_it.prev_row() != pr_it.row()); word_res->word->set_flag(W_EOL, pr_it.next_row() != pr_it.row()); } if (applybox_debug > 0) { tprintf(" Found %d good blobs.\n", ok_blob_count); if (bad_blob_count > 0) { tprintf(" Leaving %d unlabelled blobs in %d words.\n", bad_blob_count, ok_word_count); } if (unlabelled_words > 0) tprintf(" %d remaining unlabelled words deleted.\n", unlabelled_words); } } /** Logs a bad box by line in the box file and box coords.*/ void Tesseract::ReportFailedBox(int boxfile_lineno, TBOX box, const char *box_ch, const char *err_msg) { tprintf("APPLY_BOXES: boxfile line %d/%s ((%d,%d),(%d,%d)): %s\n", boxfile_lineno + 1, box_ch, box.left(), box.bottom(), box.right(), box.top(), err_msg); } /** Creates a fake best_choice entry in each WERD_RES with the correct text.*/ void Tesseract::CorrectClassifyWords(PAGE_RES* page_res) { PAGE_RES_IT pr_it(page_res); for (WERD_RES *word_res = pr_it.word(); word_res != NULL; word_res = pr_it.forward()) { WERD_CHOICE* choice = new WERD_CHOICE(word_res->uch_set, word_res->correct_text.size()); for (int i = 0; i < word_res->correct_text.size(); ++i) { // The part before the first space is the real ground truth, and the // rest is the bounding box location and page number. 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, word_res->best_state[i], 0.0f, 0.0f); } word_res->ClearWordChoices(); word_res->LogNewRawChoice(choice); word_res->LogNewCookedChoice(1, false, choice); } } /// Calls #LearnWord to extract features for labelled blobs within each word. /// Features are stored in an internal buffer. void Tesseract::ApplyBoxTraining(const STRING& fontname, PAGE_RES* page_res) { PAGE_RES_IT pr_it(page_res); int word_count = 0; for (WERD_RES *word_res = pr_it.word(); word_res != NULL; word_res = pr_it.forward()) { LearnWord(fontname.string(), word_res); ++word_count; } tprintf("Generated training data for %d words\n", word_count); } } // namespace tesseract tesseract-3.04.01/ccmain/control.cpp000066400000000000000000002266301266071204500173060ustar00rootroot00000000000000/****************************************************************** * File: control.cpp (Formerly control.c) * Description: Module-independent matcher controller. * Author: Ray Smith * Created: Thu Apr 23 11:09:58 BST 1992 * ReHacked: Tue Sep 22 08:42:49 BST 1992 Phil Cheatle * * (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. * **********************************************************************/ // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include #include #ifdef __UNIX__ #include #include #include #endif #include #include "ocrclass.h" #include "werdit.h" #include "drawfx.h" #include "tessbox.h" #include "tessvars.h" #include "pgedit.h" #include "reject.h" #include "fixspace.h" #include "docqual.h" #include "control.h" #include "output.h" #include "callcpp.h" #include "globals.h" #include "sorthelper.h" #include "tesseractclass.h" #define MIN_FONT_ROW_COUNT 8 #define MAX_XHEIGHT_DIFF 3 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; /** * Make a word from the selected blobs and run Tess on them. * * @param page_res recognise blobs * @param selection_box within this box */ namespace tesseract { void Tesseract::recog_pseudo_word(PAGE_RES* page_res, TBOX &selection_box) { PAGE_RES_IT* it = make_pseudo_word(page_res, selection_box); if (it != NULL) { recog_interactive(it); it->DeleteCurrentWord(); delete it; } } /** * Recognize a single word in interactive mode. * * @param pr_it the page results iterator */ BOOL8 Tesseract::recog_interactive(PAGE_RES_IT* pr_it) { inT16 char_qual; inT16 good_char_qual; WordData word_data(*pr_it); SetupWordPassN(2, &word_data); classify_word_and_language(2, pr_it, &word_data); if (tessedit_debug_quality_metrics) { WERD_RES* word_res = pr_it->word(); word_char_quality(word_res, pr_it->row()->row, &char_qual, &good_char_qual); tprintf("\n%d chars; word_blob_quality: %d; outline_errs: %d; " "char_quality: %d; good_char_quality: %d\n", word_res->reject_map.length(), word_blob_quality(word_res, pr_it->row()->row), word_outline_errs(word_res), char_qual, good_char_qual); } return TRUE; } // Helper function to check for a target word and handle it appropriately. // Inspired by Jetsoft's requirement to process only single words on pass2 // and beyond. // If word_config is not null: // If the word_box and target_word_box overlap, read the word_config file // else reset to previous config data. // return true. // else // If the word_box and target_word_box overlap or pass <= 1, return true. // Note that this function uses a fixed temporary file for storing the previous // configs, so it is neither thread-safe, nor process-safe, but the assumption // is that it will only be used for one debug window at a time. // // Since this function is used for debugging (and not to change OCR results) // set only debug params from the word config file. bool Tesseract::ProcessTargetWord(const TBOX& word_box, const TBOX& target_word_box, const char* word_config, int pass) { if (word_config != NULL) { if (word_box.major_overlap(target_word_box)) { if (backup_config_file_ == NULL) { backup_config_file_ = kBackUpConfigFile; FILE* config_fp = fopen(backup_config_file_, "wb"); ParamUtils::PrintParams(config_fp, params()); fclose(config_fp); ParamUtils::ReadParamsFile(word_config, SET_PARAM_CONSTRAINT_DEBUG_ONLY, params()); } } else { if (backup_config_file_ != NULL) { ParamUtils::ReadParamsFile(backup_config_file_, SET_PARAM_CONSTRAINT_DEBUG_ONLY, params()); backup_config_file_ = NULL; } } } else if (pass > 1 && !word_box.major_overlap(target_word_box)) { return false; } return true; } /** If tesseract is to be run, sets the words up ready for it. */ void Tesseract::SetupAllWordsPassN(int pass_n, const TBOX* target_word_box, const char* word_config, PAGE_RES* page_res, GenericVector* words) { // Prepare all the words. PAGE_RES_IT page_res_it(page_res); for (page_res_it.restart_page(); page_res_it.word() != NULL; page_res_it.forward()) { if (target_word_box == NULL || ProcessTargetWord(page_res_it.word()->word->bounding_box(), *target_word_box, word_config, 1)) { words->push_back(WordData(page_res_it)); } } // Setup all the words for recognition with polygonal approximation. for (int w = 0; w < words->size(); ++w) { SetupWordPassN(pass_n, &(*words)[w]); if (w > 0) (*words)[w].prev_word = &(*words)[w - 1]; } } // Sets up the single word ready for whichever engine is to be run. void Tesseract::SetupWordPassN(int pass_n, WordData* word) { if (pass_n == 1 || !word->word->done) { if (pass_n == 1) { word->word->SetupForRecognition(unicharset, this, BestPix(), tessedit_ocr_engine_mode, NULL, classify_bln_numeric_mode, textord_use_cjk_fp_model, poly_allow_detailed_fx, word->row, word->block); } else if (pass_n == 2) { // TODO(rays) Should we do this on pass1 too? word->word->caps_height = 0.0; if (word->word->x_height == 0.0f) word->word->x_height = word->row->x_height(); } word->lang_words.truncate(0); for (int s = 0; s <= sub_langs_.size(); ++s) { // The sub_langs_.size() entry is for the master language. Tesseract* lang_t = s < sub_langs_.size() ? sub_langs_[s] : this; WERD_RES* word_res = new WERD_RES; word_res->InitForRetryRecognition(*word->word); word->lang_words.push_back(word_res); // Cube doesn't get setup for pass2. if (pass_n == 1 || lang_t->tessedit_ocr_engine_mode != OEM_CUBE_ONLY) { word_res->SetupForRecognition( lang_t->unicharset, lang_t, BestPix(), lang_t->tessedit_ocr_engine_mode, NULL, lang_t->classify_bln_numeric_mode, lang_t->textord_use_cjk_fp_model, lang_t->poly_allow_detailed_fx, word->row, word->block); } } } } // Runs word recognition on all the words. bool Tesseract::RecogAllWordsPassN(int pass_n, ETEXT_DESC* monitor, PAGE_RES_IT* pr_it, GenericVector* words) { // TODO(rays) Before this loop can be parallelized (it would yield a massive // speed-up) all remaining member globals need to be converted to local/heap // (eg set_pass1 and set_pass2) and an intermediate adaption pass needs to be // added. The results will be significantly different with adaption on, and // deterioration will need investigation. pr_it->restart_page(); for (int w = 0; w < words->size(); ++w) { WordData* word = &(*words)[w]; if (w > 0) word->prev_word = &(*words)[w - 1]; if (monitor != NULL) { monitor->ocr_alive = TRUE; if (pass_n == 1) monitor->progress = 30 + 50 * w / words->size(); else monitor->progress = 80 + 10 * w / words->size(); if (monitor->deadline_exceeded() || (monitor->cancel != NULL && (*monitor->cancel)(monitor->cancel_this, words->size()))) { // Timeout. Fake out the rest of the words. for (; w < words->size(); ++w) { (*words)[w].word->SetupFake(unicharset); } return false; } } if (word->word->tess_failed) { int s; for (s = 0; s < word->lang_words.size() && word->lang_words[s]->tess_failed; ++s) {} // If all are failed, skip it. Image words are skipped by this test. if (s > word->lang_words.size()) continue; } // Sync pr_it with the wth WordData. while (pr_it->word() != NULL && pr_it->word() != word->word) pr_it->forward(); ASSERT_HOST(pr_it->word() != NULL); bool make_next_word_fuzzy = false; if (ReassignDiacritics(pass_n, pr_it, &make_next_word_fuzzy)) { // Needs to be setup again to see the new outlines in the chopped_word. SetupWordPassN(pass_n, word); } classify_word_and_language(pass_n, pr_it, word); if (tessedit_dump_choices || debug_noise_removal) { tprintf("Pass%d: %s [%s]\n", pass_n, word->word->best_choice->unichar_string().string(), word->word->best_choice->debug_string().string()); } pr_it->forward(); if (make_next_word_fuzzy && pr_it->word() != NULL) { pr_it->MakeCurrentWordFuzzy(); } } return true; } /** * recog_all_words() * * Walk the page_res, recognizing all the words. * If monitor is not null, it is used as a progress monitor/timeout/cancel. * If dopasses is 0, all recognition passes are run, * 1 just pass 1, 2 passes2 and higher. * If target_word_box is not null, special things are done to words that * overlap the target_word_box: * if word_config is not null, the word config file is read for just the * target word(s), otherwise, on pass 2 and beyond ONLY the target words * are processed (Jetsoft modification.) * Returns false if we cancelled prematurely. * * @param page_res page structure * @param monitor progress monitor * @param word_config word_config file * @param target_word_box specifies just to extract a rectangle * @param dopasses 0 - all, 1 just pass 1, 2 passes 2 and higher */ bool Tesseract::recog_all_words(PAGE_RES* page_res, ETEXT_DESC* monitor, const TBOX* target_word_box, const char* word_config, int dopasses) { PAGE_RES_IT page_res_it(page_res); if (tessedit_minimal_rej_pass1) { tessedit_test_adaption.set_value (TRUE); tessedit_minimal_rejection.set_value (TRUE); } if (dopasses==0 || dopasses==1) { page_res_it.restart_page(); // ****************** Pass 1 ******************* // If the adaptive classifier is full switch to one we prepared earlier, // ie on the previous page. If the current adaptive classifier is non-empty, // prepare a backup starting at this page, in case it fills up. Do all this // independently for each language. if (AdaptiveClassifierIsFull()) { SwitchAdaptiveClassifier(); } else if (!AdaptiveClassifierIsEmpty()) { StartBackupAdaptiveClassifier(); } // Now check the sub-langs as well. for (int i = 0; i < sub_langs_.size(); ++i) { if (sub_langs_[i]->AdaptiveClassifierIsFull()) { sub_langs_[i]->SwitchAdaptiveClassifier(); } else if (!sub_langs_[i]->AdaptiveClassifierIsEmpty()) { sub_langs_[i]->StartBackupAdaptiveClassifier(); } } // Set up all words ready for recognition, so that if parallelism is on // all the input and output classes are ready to run the classifier. GenericVector words; SetupAllWordsPassN(1, target_word_box, word_config, page_res, &words); if (tessedit_parallelize) { PrerecAllWordsPar(words); } stats_.word_count = words.size(); stats_.dict_words = 0; stats_.doc_blob_quality = 0; stats_.doc_outline_errs = 0; stats_.doc_char_quality = 0; stats_.good_char_count = 0; stats_.doc_good_char_quality = 0; most_recently_used_ = this; // Run pass 1 word recognition. if (!RecogAllWordsPassN(1, monitor, &page_res_it, &words)) return false; // Pass 1 post-processing. for (page_res_it.restart_page(); page_res_it.word() != NULL; page_res_it.forward()) { if (page_res_it.word()->word->flag(W_REP_CHAR)) { fix_rep_char(&page_res_it); continue; } // Count dict words. if (page_res_it.word()->best_choice->permuter() == USER_DAWG_PERM) ++(stats_.dict_words); // 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->misadaption_log.push_back( page_res_it.word()->blamer_bundle->misadaption_debug()); } } } if (dopasses == 1) return true; // ****************** Pass 2 ******************* if (tessedit_tess_adaption_mode != 0x0 && !tessedit_test_adaption && AnyTessLang()) { page_res_it.restart_page(); GenericVector words; SetupAllWordsPassN(2, target_word_box, word_config, page_res, &words); if (tessedit_parallelize) { PrerecAllWordsPar(words); } most_recently_used_ = this; // Run pass 2 word recognition. if (!RecogAllWordsPassN(2, monitor, &page_res_it, &words)) return false; } // The next passes can only be run if tesseract has been used, as cube // doesn't set all the necessary outputs in WERD_RES. if (AnyTessLang()) { // ****************** Pass 3 ******************* // Fix fuzzy spaces. set_global_loc_code(LOC_FUZZY_SPACE); if (!tessedit_test_adaption && tessedit_fix_fuzzy_spaces && !tessedit_word_for_word && !right_to_left()) fix_fuzzy_spaces(monitor, stats_.word_count, page_res); // ****************** Pass 4 ******************* if (tessedit_enable_dict_correction) dictionary_correction_pass(page_res); if (tessedit_enable_bigram_correction) bigram_correction_pass(page_res); // ****************** Pass 5,6 ******************* rejection_passes(page_res, monitor, target_word_box, word_config); #ifndef NO_CUBE_BUILD // ****************** Pass 7 ******************* // Cube combiner. // If cube is loaded and its combiner is present, run it. if (tessedit_ocr_engine_mode == OEM_TESSERACT_CUBE_COMBINED) { run_cube_combiner(page_res); } #endif // ****************** Pass 8 ******************* font_recognition_pass(page_res); // ****************** Pass 9 ******************* // Check the correctness of the final results. blamer_pass(page_res); script_pos_pass(page_res); } // Write results pass. set_global_loc_code(LOC_WRITE_RESULTS); // This is now redundant, but retained commented so show how to obtain // bounding boxes and style information. // changed by jetsoft // needed for dll to output memory structure if ((dopasses == 0 || dopasses == 2) && (monitor || tessedit_write_unlv)) output_pass(page_res_it, target_word_box); // end jetsoft PageSegMode pageseg_mode = static_cast( static_cast(tessedit_pageseg_mode)); textord_.CleanupSingleRowResult(pageseg_mode, page_res); // Remove empty words, as these mess up the result iterators. for (page_res_it.restart_page(); page_res_it.word() != NULL; page_res_it.forward()) { WERD_RES* word = page_res_it.word(); if (word->best_choice == NULL || word->best_choice->length() == 0) page_res_it.DeleteCurrentWord(); } if (monitor != NULL) { monitor->progress = 100; } return true; } void Tesseract::bigram_correction_pass(PAGE_RES *page_res) { PAGE_RES_IT word_it(page_res); WERD_RES *w_prev = NULL; WERD_RES *w = word_it.word(); while (1) { w_prev = w; while (word_it.forward() != NULL && (!word_it.word() || word_it.word()->part_of_combo)) { // advance word_it, skipping over parts of combos } if (!word_it.word()) break; w = word_it.word(); if (!w || !w_prev || w->uch_set != w_prev->uch_set) { continue; } if (w_prev->word->flag(W_REP_CHAR) || w->word->flag(W_REP_CHAR)) { if (tessedit_bigram_debug) { tprintf("Skipping because one of the words is W_REP_CHAR\n"); } continue; } // Two words sharing the same language model, excellent! GenericVector overrides_word1; GenericVector overrides_word2; 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->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->best_choice->GetNonSuperscriptSpan(&w2start, &w2end); this_best = w->best_choice->shallow_copy(w2start, w2end); } if (w->tesseract->getDict().valid_bigram(prev_best, this_best)) { if (tessedit_bigram_debug) { tprintf("Top choice \"%s %s\" verified by bigram model.\n", orig_w1_str.string(), orig_w2_str.string()); } continue; } if (tessedit_bigram_debug > 2) { tprintf("Examining alt choices for \"%s %s\".\n", orig_w1_str.string(), orig_w2_str.string()); } if (tessedit_bigram_debug > 1) { if (!w_prev->best_choices.singleton()) { w_prev->PrintBestChoices(); } if (!w->best_choices.singleton()) { w->PrintBestChoices(); } } float best_rating = 0.0; int best_idx = 0; 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; p1->GetNonSuperscriptSpan(&p1start, &p1end); strip1 = p1->shallow_copy(p1start, p1end); } 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; p2->GetNonSuperscriptSpan(&p2start, &p2end); strip2 = p2->shallow_copy(p2start, p2end); } if (w->tesseract->getDict().valid_bigram(strip1, strip2)) { overrides_word1.push_back(p1); overrides_word2.push_back(p2); if (overrides_word1.size() == 1 || p1->rating() + p2->rating() < best_rating) { best_rating = p1->rating() + p2->rating(); best_idx = overrides_word1.size() - 1; } } } } if (overrides_word1.size() >= 1) { // Excellent, we have some bigram matches. if (EqualIgnoringCaseAndTerminalPunct(*w_prev->best_choice, *overrides_word1[best_idx]) && EqualIgnoringCaseAndTerminalPunct(*w->best_choice, *overrides_word2[best_idx])) { if (tessedit_bigram_debug > 1) { tprintf("Top choice \"%s %s\" verified (sans case) by bigram " "model.\n", orig_w1_str.string(), orig_w2_str.string()); } continue; } 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]); } if (new_w2_str != orig_w2_str) { w->ReplaceBestChoice(overrides_word2[best_idx]); } if (tessedit_bigram_debug > 0) { STRING choices_description; int num_bigram_choices = overrides_word1.size() * overrides_word2.size(); if (num_bigram_choices == 1) { choices_description = "This was the unique bigram choice."; } else { if (tessedit_bigram_debug > 1) { STRING bigrams_list; const int kMaxChoicesToPrint = 20; for (int i = 0; i < overrides_word1.size() && i < kMaxChoicesToPrint; i++) { if (i > 0) { bigrams_list += ", "; } WERD_CHOICE *p1 = overrides_word1[i]; WERD_CHOICE *p2 = overrides_word2[i]; bigrams_list += p1->unichar_string() + " " + p2->unichar_string(); if (i == kMaxChoicesToPrint) { bigrams_list += " ..."; } } choices_description = "There were many choices: {"; choices_description += bigrams_list; choices_description += "}"; } else { choices_description.add_str_int("There were ", num_bigram_choices); choices_description += " compatible bigrams."; } } tprintf("Replaced \"%s %s\" with \"%s %s\" with bigram model. %s\n", orig_w1_str.string(), orig_w2_str.string(), new_w1_str.string(), new_w2_str.string(), choices_description.string()); } } } } void Tesseract::rejection_passes(PAGE_RES* page_res, ETEXT_DESC* monitor, const TBOX* target_word_box, const char* word_config) { PAGE_RES_IT page_res_it(page_res); // ****************** Pass 5 ******************* // Gather statistics on rejects. int word_index = 0; while (!tessedit_test_adaption && page_res_it.word() != NULL) { set_global_loc_code(LOC_MM_ADAPT); WERD_RES* word = page_res_it.word(); word_index++; if (monitor != NULL) { monitor->ocr_alive = TRUE; monitor->progress = 95 + 5 * word_index / stats_.word_count; } if (word->rebuild_word == NULL) { // Word was not processed by tesseract. page_res_it.forward(); continue; } check_debug_pt(word, 70); // changed by jetsoft // specific to its needs to extract one word when need if (target_word_box && !ProcessTargetWord(word->word->bounding_box(), *target_word_box, word_config, 4)) { page_res_it.forward(); continue; } // end jetsoft page_res_it.rej_stat_word(); int chars_in_word = word->reject_map.length(); int rejects_in_word = word->reject_map.reject_count(); int blob_quality = word_blob_quality(word, page_res_it.row()->row); stats_.doc_blob_quality += blob_quality; int outline_errs = word_outline_errs(word); stats_.doc_outline_errs += outline_errs; inT16 all_char_quality; inT16 accepted_all_char_quality; word_char_quality(word, page_res_it.row()->row, &all_char_quality, &accepted_all_char_quality); stats_.doc_char_quality += all_char_quality; uinT8 permuter_type = word->best_choice->permuter(); if ((permuter_type == SYSTEM_DAWG_PERM) || (permuter_type == FREQ_DAWG_PERM) || (permuter_type == USER_DAWG_PERM)) { stats_.good_char_count += chars_in_word - rejects_in_word; stats_.doc_good_char_quality += accepted_all_char_quality; } check_debug_pt(word, 80); if (tessedit_reject_bad_qual_wds && (blob_quality == 0) && (outline_errs >= chars_in_word)) word->reject_map.rej_word_bad_quality(); check_debug_pt(word, 90); page_res_it.forward(); } if (tessedit_debug_quality_metrics) { tprintf ("QUALITY: num_chs= %d num_rejs= %d %5.3f blob_qual= %d %5.3f" " outline_errs= %d %5.3f char_qual= %d %5.3f good_ch_qual= %d %5.3f\n", page_res->char_count, page_res->rej_count, page_res->rej_count / static_cast(page_res->char_count), stats_.doc_blob_quality, stats_.doc_blob_quality / static_cast(page_res->char_count), stats_.doc_outline_errs, stats_.doc_outline_errs / static_cast(page_res->char_count), stats_.doc_char_quality, stats_.doc_char_quality / static_cast(page_res->char_count), stats_.doc_good_char_quality, (stats_.good_char_count > 0) ? (stats_.doc_good_char_quality / static_cast(stats_.good_char_count)) : 0.0); } BOOL8 good_quality_doc = ((page_res->rej_count / static_cast(page_res->char_count)) <= quality_rej_pc) && (stats_.doc_blob_quality / static_cast(page_res->char_count) >= quality_blob_pc) && (stats_.doc_outline_errs / static_cast(page_res->char_count) <= quality_outline_pc) && (stats_.doc_char_quality / static_cast(page_res->char_count) >= quality_char_pc); // ****************** Pass 6 ******************* // Do whole document or whole block rejection pass if (!tessedit_test_adaption) { set_global_loc_code(LOC_DOC_BLK_REJ); quality_based_rejection(page_res_it, good_quality_doc); } } void Tesseract::blamer_pass(PAGE_RES* page_res) { if (!wordrec_run_blamer) return; PAGE_RES_IT page_res_it(page_res); for (page_res_it.restart_page(); page_res_it.word() != NULL; page_res_it.forward()) { WERD_RES *word = page_res_it.word(); 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) { tprintf("%s %d\n", BlamerBundle::IncorrectReasonName( static_cast(bl)), page_res->blame_reasons[bl]); } if (page_res->misadaption_log.length() > 0) { tprintf("Misadaption log:\n"); for (int i = 0; i < page_res->misadaption_log.length(); ++i) { tprintf("%s\n", page_res->misadaption_log[i].string()); } } } // Sets script positions and detects smallcaps on all output words. void Tesseract::script_pos_pass(PAGE_RES* page_res) { PAGE_RES_IT page_res_it(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->word->flag(W_REP_CHAR)) { page_res_it.forward(); continue; } float x_height = page_res_it.block()->block->x_height(); float word_x_height = word->x_height; if (word_x_height < word->best_choice->min_x_height() || word_x_height > word->best_choice->max_x_height()) { word_x_height = (word->best_choice->min_x_height() + word->best_choice->max_x_height()) / 2.0f; } // 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 = x_height * kXHeightCapRatio; double small_cap_delta = (x_height - small_cap_xheight) / 2.0; if (word->uch_set->script_has_xheight() && small_cap_xheight - small_cap_delta <= word_x_height && word_x_height <= small_cap_xheight + small_cap_delta) { // Scan for upper/lower. int num_upper = 0; int num_lower = 0; for (int i = 0; i < word->best_choice->length(); ++i) { if (word->uch_set->get_isupper(word->best_choice->unichar_id(i))) ++num_upper; else if (word->uch_set->get_islower(word->best_choice->unichar_id(i))) ++num_lower; } if (num_upper > 0 && num_lower == 0) word->small_caps = true; } word->SetScriptPositions(); } } // Factored helper considers the indexed word and updates all the pointed // values. static void EvaluateWord(const PointerVector& words, int index, float* rating, float* certainty, bool* bad, bool* valid_permuter, int* right, int* next_left) { *right = -MAX_INT32; *next_left = MAX_INT32; if (index < words.size()) { WERD_CHOICE* choice = words[index]->best_choice; if (choice == NULL) { *bad = true; } else { *rating += choice->rating(); *certainty = MIN(*certainty, choice->certainty()); if (!Dict::valid_word_permuter(choice->permuter(), false)) *valid_permuter = false; } *right = words[index]->word->bounding_box().right(); if (index + 1 < words.size()) *next_left = words[index + 1]->word->bounding_box().left(); } else { *valid_permuter = false; *bad = true; } } // Helper chooses the best combination of words, transferring good ones from // new_words to best_words. To win, a new word must have (better rating and // certainty) or (better permuter status and rating within rating ratio and // certainty within certainty margin) than current best. // All the new_words are consumed (moved to best_words or deleted.) // The return value is the number of new_words used minus the number of // best_words that remain in the output. static int SelectBestWords(double rating_ratio, double certainty_margin, bool debug, PointerVector* new_words, PointerVector* best_words) { // Process the smallest groups of words that have an overlapping word // boundary at the end. GenericVector out_words; // Index into each word vector (best, new). int b = 0, n = 0; int num_best = 0, num_new = 0; while (b < best_words->size() || n < new_words->size()) { // Start of the current run in each. int start_b = b, start_n = n; // Rating of the current run in each. float b_rating = 0.0f, n_rating = 0.0f; // Certainty of the current run in each. float b_certainty = 0.0f, n_certainty = 0.0f; // True if any word is missing its best choice. bool b_bad = false, n_bad = false; // True if all words have a valid permuter. bool b_valid_permuter = true, n_valid_permuter = true; while (b < best_words->size() || n < new_words->size()) { int b_right = -MAX_INT32; int next_b_left = MAX_INT32; EvaluateWord(*best_words, b, &b_rating, &b_certainty, &b_bad, &b_valid_permuter, &b_right, &next_b_left); int n_right = -MAX_INT32; int next_n_left = MAX_INT32; EvaluateWord(*new_words, n, &n_rating, &n_certainty, &n_bad, &n_valid_permuter, &n_right, &next_n_left); if (MAX(b_right, n_right) < MIN(next_b_left, next_n_left)) { // The word breaks overlap. [start_b,b] and [start_n, n] match. break; } // Keep searching for the matching word break. if ((b_right < n_right && b < best_words->size()) || n == new_words->size()) ++b; else ++n; } bool new_better = false; if (!n_bad && (b_bad || (n_certainty > b_certainty && n_rating < b_rating) || (!b_valid_permuter && n_valid_permuter && n_rating < b_rating * rating_ratio && n_certainty > b_certainty - certainty_margin))) { // New is better. for (int i = start_n; i <= n; ++i) { out_words.push_back((*new_words)[i]); (*new_words)[i] = NULL; ++num_new; } new_better = true; } else if (!b_bad) { // Current best is better. for (int i = start_b; i <= b; ++i) { out_words.push_back((*best_words)[i]); (*best_words)[i] = NULL; ++num_best; } } int end_b = b < best_words->size() ? b + 1 : b; int end_n = n < new_words->size() ? n + 1 : n; if (debug) { tprintf("%d new words %s than %d old words: r: %g v %g c: %g v %g" " valid dict: %d v %d\n", end_n - start_n, new_better ? "better" : "worse", end_b - start_b, n_rating, b_rating, n_certainty, b_certainty, n_valid_permuter, b_valid_permuter); } // Move on to the next group. b = end_b; n = end_n; } // Transfer from out_words to best_words. best_words->clear(); for (int i = 0; i < out_words.size(); ++i) best_words->push_back(out_words[i]); return num_new - num_best; } // Helper to recognize the word using the given (language-specific) tesseract. // Returns positive if this recognizer found more new best words than the // number kept from best_words. int Tesseract::RetryWithLanguage(const WordData& word_data, WordRecognizer recognizer, WERD_RES** in_word, PointerVector* best_words) { bool debug = classify_debug_level || cube_debug_level; if (debug) { tprintf("Trying word using lang %s, oem %d\n", lang.string(), static_cast(tessedit_ocr_engine_mode)); } // Run the recognizer on the word. PointerVector new_words; (this->*recognizer)(word_data, in_word, &new_words); if (new_words.empty()) { // Transfer input word to new_words, as the classifier must have put // the result back in the input. new_words.push_back(*in_word); *in_word = NULL; } if (debug) { for (int i = 0; i < new_words.size(); ++i) new_words[i]->DebugTopChoice("Lang result"); } // Initial version is a bit of a hack based on better certainty and rating // (to reduce false positives from cube) or a dictionary vs non-dictionary // word. return SelectBestWords(classify_max_rating_ratio, classify_max_certainty_margin, debug, &new_words, best_words); } // Helper returns true if all the words are acceptable. static bool WordsAcceptable(const PointerVector& words) { for (int w = 0; w < words.size(); ++w) { if (words[w]->tess_failed || !words[w]->tess_accepted) return false; } return true; } // Moves good-looking "noise"/diacritics from the reject list to the main // blob list on the current word. Returns true if anything was done, and // sets make_next_word_fuzzy if blob(s) were added to the end of the word. bool Tesseract::ReassignDiacritics(int pass, PAGE_RES_IT* pr_it, bool* make_next_word_fuzzy) { *make_next_word_fuzzy = false; WERD* real_word = pr_it->word()->word; if (real_word->rej_cblob_list()->empty() || real_word->cblob_list()->empty() || real_word->rej_cblob_list()->length() > noise_maxperword) return false; real_word->rej_cblob_list()->sort(&C_BLOB::SortByXMiddle); // Get the noise outlines into a vector with matching bool map. GenericVector outlines; real_word->GetNoiseOutlines(&outlines); GenericVector word_wanted; GenericVector overlapped_any_blob; GenericVector target_blobs; AssignDiacriticsToOverlappingBlobs(outlines, pass, real_word, pr_it, &word_wanted, &overlapped_any_blob, &target_blobs); // Filter the outlines that overlapped any blob and put them into the word // now. This simplifies the remaining task and also makes it more accurate // as it has more completed blobs to work on. GenericVector wanted; GenericVector wanted_blobs; GenericVector wanted_outlines; int num_overlapped = 0; int num_overlapped_used = 0; for (int i = 0; i < overlapped_any_blob.size(); ++i) { if (overlapped_any_blob[i]) { ++num_overlapped; if (word_wanted[i]) ++num_overlapped_used; wanted.push_back(word_wanted[i]); wanted_blobs.push_back(target_blobs[i]); wanted_outlines.push_back(outlines[i]); outlines[i] = NULL; } } real_word->AddSelectedOutlines(wanted, wanted_blobs, wanted_outlines, NULL); AssignDiacriticsToNewBlobs(outlines, pass, real_word, pr_it, &word_wanted, &target_blobs); int non_overlapped = 0; int non_overlapped_used = 0; for (int i = 0; i < word_wanted.size(); ++i) { if (word_wanted[i]) ++non_overlapped_used; if (outlines[i] != NULL) ++non_overlapped_used; } if (debug_noise_removal) { tprintf("Used %d/%d overlapped %d/%d non-overlaped diacritics on word:", num_overlapped_used, num_overlapped, non_overlapped_used, non_overlapped); real_word->bounding_box().print(); } // Now we have decided which outlines we want, put them into the real_word. if (real_word->AddSelectedOutlines(word_wanted, target_blobs, outlines, make_next_word_fuzzy)) { pr_it->MakeCurrentWordFuzzy(); } // TODO(rays) Parts of combos have a deep copy of the real word, and need // to have their noise outlines moved/assigned in the same way!! return num_overlapped_used != 0 || non_overlapped_used != 0; } // Attempts to put noise/diacritic outlines into the blobs that they overlap. // Input: a set of noisy outlines that probably belong to the real_word. // Output: word_wanted indicates which outlines are to be assigned to a blob, // target_blobs indicates which to assign to, and overlapped_any_blob is // true for all outlines that overlapped a blob. void Tesseract::AssignDiacriticsToOverlappingBlobs( const GenericVector& outlines, int pass, WERD* real_word, PAGE_RES_IT* pr_it, GenericVector* word_wanted, GenericVector* overlapped_any_blob, GenericVector* target_blobs) { GenericVector blob_wanted; word_wanted->init_to_size(outlines.size(), false); overlapped_any_blob->init_to_size(outlines.size(), false); target_blobs->init_to_size(outlines.size(), NULL); // For each real blob, find the outlines that seriously overlap it. // A single blob could be several merged characters, so there can be quite // a few outlines overlapping, and the full engine needs to be used to chop // and join to get a sensible result. C_BLOB_IT blob_it(real_word->cblob_list()); for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { C_BLOB* blob = blob_it.data(); TBOX blob_box = blob->bounding_box(); blob_wanted.init_to_size(outlines.size(), false); int num_blob_outlines = 0; for (int i = 0; i < outlines.size(); ++i) { if (blob_box.major_x_overlap(outlines[i]->bounding_box()) && !(*word_wanted)[i]) { blob_wanted[i] = true; (*overlapped_any_blob)[i] = true; ++num_blob_outlines; } } if (debug_noise_removal) { tprintf("%d noise outlines overlap blob at:", num_blob_outlines); blob_box.print(); } // If any outlines overlap the blob, and not too many, classify the blob // (using the full engine, languages and all), and choose the maximal // combination of outlines that doesn't hurt the end-result classification // by too much. Mark them as wanted. if (0 < num_blob_outlines && num_blob_outlines < noise_maxperblob) { if (SelectGoodDiacriticOutlines(pass, noise_cert_basechar, pr_it, blob, outlines, num_blob_outlines, &blob_wanted)) { for (int i = 0; i < blob_wanted.size(); ++i) { if (blob_wanted[i]) { // Claim the outline and record where it is going. (*word_wanted)[i] = true; (*target_blobs)[i] = blob; } } } } } } // Attempts to assign non-overlapping outlines to their nearest blobs or // make new blobs out of them. void Tesseract::AssignDiacriticsToNewBlobs( const GenericVector& outlines, int pass, WERD* real_word, PAGE_RES_IT* pr_it, GenericVector* word_wanted, GenericVector* target_blobs) { GenericVector blob_wanted; word_wanted->init_to_size(outlines.size(), false); target_blobs->init_to_size(outlines.size(), NULL); // Check for outlines that need to be turned into stand-alone blobs. for (int i = 0; i < outlines.size(); ++i) { if (outlines[i] == NULL) continue; // Get a set of adjacent outlines that don't overlap any existing blob. blob_wanted.init_to_size(outlines.size(), false); int num_blob_outlines = 0; TBOX total_ol_box(outlines[i]->bounding_box()); while (i < outlines.size() && outlines[i] != NULL) { blob_wanted[i] = true; total_ol_box += outlines[i]->bounding_box(); ++i; ++num_blob_outlines; } // Find the insertion point. C_BLOB_IT blob_it(real_word->cblob_list()); while (!blob_it.at_last() && blob_it.data_relative(1)->bounding_box().left() <= total_ol_box.left()) { blob_it.forward(); } // Choose which combination of them we actually want and where to put // them. if (debug_noise_removal) tprintf("Num blobless outlines = %d\n", num_blob_outlines); C_BLOB* left_blob = blob_it.data(); TBOX left_box = left_blob->bounding_box(); C_BLOB* right_blob = blob_it.at_last() ? NULL : blob_it.data_relative(1); if ((left_box.x_overlap(total_ol_box) || right_blob == NULL || !right_blob->bounding_box().x_overlap(total_ol_box)) && SelectGoodDiacriticOutlines(pass, noise_cert_disjoint, pr_it, left_blob, outlines, num_blob_outlines, &blob_wanted)) { if (debug_noise_removal) tprintf("Added to left blob\n"); for (int j = 0; j < blob_wanted.size(); ++j) { if (blob_wanted[j]) { (*word_wanted)[j] = true; (*target_blobs)[j] = left_blob; } } } else if (right_blob != NULL && (!left_box.x_overlap(total_ol_box) || right_blob->bounding_box().x_overlap(total_ol_box)) && SelectGoodDiacriticOutlines(pass, noise_cert_disjoint, pr_it, right_blob, outlines, num_blob_outlines, &blob_wanted)) { if (debug_noise_removal) tprintf("Added to right blob\n"); for (int j = 0; j < blob_wanted.size(); ++j) { if (blob_wanted[j]) { (*word_wanted)[j] = true; (*target_blobs)[j] = right_blob; } } } else if (SelectGoodDiacriticOutlines(pass, noise_cert_punc, pr_it, NULL, outlines, num_blob_outlines, &blob_wanted)) { if (debug_noise_removal) tprintf("Fitted between blobs\n"); for (int j = 0; j < blob_wanted.size(); ++j) { if (blob_wanted[j]) { (*word_wanted)[j] = true; (*target_blobs)[j] = NULL; } } } } } // Starting with ok_outlines set to indicate which outlines overlap the blob, // chooses the optimal set (approximately) and returns true if any outlines // are desired, in which case ok_outlines indicates which ones. bool Tesseract::SelectGoodDiacriticOutlines( int pass, float certainty_threshold, PAGE_RES_IT* pr_it, C_BLOB* blob, const GenericVector& outlines, int num_outlines, GenericVector* ok_outlines) { STRING best_str; float target_cert = certainty_threshold; if (blob != NULL) { float target_c2; target_cert = ClassifyBlobAsWord(pass, pr_it, blob, &best_str, &target_c2); if (debug_noise_removal) { tprintf("No Noise blob classified as %s=%g(%g) at:", best_str.string(), target_cert, target_c2); blob->bounding_box().print(); } target_cert -= (target_cert - certainty_threshold) * noise_cert_factor; } GenericVector test_outlines = *ok_outlines; // Start with all the outlines in. STRING all_str; GenericVector best_outlines = *ok_outlines; float best_cert = ClassifyBlobPlusOutlines(test_outlines, outlines, pass, pr_it, blob, &all_str); if (debug_noise_removal) { TBOX ol_box; for (int i = 0; i < test_outlines.size(); ++i) { if (test_outlines[i]) ol_box += outlines[i]->bounding_box(); } tprintf("All Noise blob classified as %s=%g, delta=%g at:", all_str.string(), best_cert, best_cert - target_cert); ol_box.print(); } // Iteratively zero out the bit that improves the certainty the most, until // we get past the threshold, have zero bits, or fail to improve. int best_index = 0; // To zero out. while (num_outlines > 1 && best_index >= 0 && (blob == NULL || best_cert < target_cert || blob != NULL)) { // Find the best bit to zero out. best_index = -1; for (int i = 0; i < outlines.size(); ++i) { if (test_outlines[i]) { test_outlines[i] = false; STRING str; float cert = ClassifyBlobPlusOutlines(test_outlines, outlines, pass, pr_it, blob, &str); if (debug_noise_removal) { TBOX ol_box; for (int j = 0; j < outlines.size(); ++j) { if (test_outlines[j]) ol_box += outlines[j]->bounding_box(); tprintf("%d", test_outlines[j]); } tprintf(" blob classified as %s=%g, delta=%g) at:", str.string(), cert, cert - target_cert); ol_box.print(); } if (cert > best_cert) { best_cert = cert; best_index = i; best_outlines = test_outlines; } test_outlines[i] = true; } } if (best_index >= 0) { test_outlines[best_index] = false; --num_outlines; } } if (best_cert >= target_cert) { // Save the best combination. *ok_outlines = best_outlines; if (debug_noise_removal) { tprintf("%s noise combination ", blob ? "Adding" : "New"); for (int i = 0; i < best_outlines.size(); ++i) { tprintf("%d", best_outlines[i]); } tprintf(" yields certainty %g, beating target of %g\n", best_cert, target_cert); } return true; } return false; } // Classifies the given blob plus the outlines flagged by ok_outlines, undoes // the inclusion of the outlines, and returns the certainty of the raw choice. float Tesseract::ClassifyBlobPlusOutlines( const GenericVector& ok_outlines, const GenericVector& outlines, int pass_n, PAGE_RES_IT* pr_it, C_BLOB* blob, STRING* best_str) { C_OUTLINE_IT ol_it; C_OUTLINE* first_to_keep = NULL; if (blob != NULL) { // Add the required outlines to the blob. ol_it.set_to_list(blob->out_list()); first_to_keep = ol_it.data(); } for (int i = 0; i < ok_outlines.size(); ++i) { if (ok_outlines[i]) { // This outline is to be added. if (blob == NULL) { blob = new C_BLOB(outlines[i]); ol_it.set_to_list(blob->out_list()); } else { ol_it.add_before_stay_put(outlines[i]); } } } float c2; float cert = ClassifyBlobAsWord(pass_n, pr_it, blob, best_str, &c2); ol_it.move_to_first(); if (first_to_keep == NULL) { // We created blob. Empty its outlines and delete it. for (; !ol_it.empty(); ol_it.forward()) ol_it.extract(); delete blob; cert = -c2; } else { // Remove the outlines that we put in. for (; ol_it.data() != first_to_keep; ol_it.forward()) { ol_it.extract(); } } return cert; } // Classifies the given blob (part of word_data->word->word) as an individual // word, using languages, chopper etc, returning only the certainty of the // best raw choice, and undoing all the work done to fake out the word. float Tesseract::ClassifyBlobAsWord(int pass_n, PAGE_RES_IT* pr_it, C_BLOB* blob, STRING* best_str, float* c2) { WERD* real_word = pr_it->word()->word; WERD* word = real_word->ConstructFromSingleBlob( real_word->flag(W_BOL), real_word->flag(W_EOL), C_BLOB::deep_copy(blob)); WERD_RES* word_res = pr_it->InsertSimpleCloneWord(*pr_it->word(), word); // Get a new iterator that points to the new word. PAGE_RES_IT it(pr_it->page_res); while (it.word() != word_res && it.word() != NULL) it.forward(); ASSERT_HOST(it.word() == word_res); WordData wd(it); // Force full initialization. SetupWordPassN(1, &wd); classify_word_and_language(pass_n, &it, &wd); if (debug_noise_removal) { tprintf("word xheight=%g, row=%g, range=[%g,%g]\n", word_res->x_height, wd.row->x_height(), wd.word->raw_choice->min_x_height(), wd.word->raw_choice->max_x_height()); } float cert = wd.word->raw_choice->certainty(); float rat = wd.word->raw_choice->rating(); *c2 = rat > 0.0f ? cert * cert / rat : 0.0f; *best_str = wd.word->raw_choice->unichar_string(); it.DeleteCurrentWord(); pr_it->ResetWordIterator(); return cert; } // Generic function for classifying a word. Can be used either for pass1 or // pass2 according to the function passed to recognizer. // word_data holds the word to be recognized, and its block and row, and // pr_it points to the word as well, in case we are running LSTM and it wants // to output multiple words. // Recognizes in the current language, and if successful that is all. // If recognition was not successful, tries all available languages until // it gets a successful result or runs out of languages. Keeps the best result. void Tesseract::classify_word_and_language(int pass_n, PAGE_RES_IT* pr_it, WordData* word_data) { WordRecognizer recognizer = pass_n == 1 ? &Tesseract::classify_word_pass1 : &Tesseract::classify_word_pass2; // Best result so far. PointerVector best_words; // Points to the best result. May be word or in lang_words. WERD_RES* word = word_data->word; clock_t start_t = clock(); if (classify_debug_level || cube_debug_level) { tprintf("%s word with lang %s at:", word->done ? "Already done" : "Processing", most_recently_used_->lang.string()); word->word->bounding_box().print(); } if (word->done) { // If done on pass1, leave it as-is. if (!word->tess_failed) most_recently_used_ = word->tesseract; return; } int sub = sub_langs_.size(); if (most_recently_used_ != this) { // Get the index of the most_recently_used_. for (sub = 0; sub < sub_langs_.size() && most_recently_used_ != sub_langs_[sub]; ++sub) {} } most_recently_used_->RetryWithLanguage( *word_data, recognizer, &word_data->lang_words[sub], &best_words); Tesseract* best_lang_tess = most_recently_used_; if (!WordsAcceptable(best_words)) { // Try all the other languages to see if they are any better. if (most_recently_used_ != this && this->RetryWithLanguage(*word_data, recognizer, &word_data->lang_words[sub_langs_.size()], &best_words) > 0) { best_lang_tess = this; } for (int i = 0; !WordsAcceptable(best_words) && i < sub_langs_.size(); ++i) { if (most_recently_used_ != sub_langs_[i] && sub_langs_[i]->RetryWithLanguage(*word_data, recognizer, &word_data->lang_words[i], &best_words) > 0) { best_lang_tess = sub_langs_[i]; } } } most_recently_used_ = best_lang_tess; if (!best_words.empty()) { if (best_words.size() == 1 && !best_words[0]->combination) { // Move the best single result to the main word. word_data->word->ConsumeWordResults(best_words[0]); } else { // Words came from LSTM, and must be moved to the PAGE_RES properly. word_data->word = best_words.back(); pr_it->ReplaceCurrentWord(&best_words); } ASSERT_HOST(word_data->word->box_word != NULL); } else { tprintf("no best words!!\n"); } 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); } } /** * classify_word_pass1 * * Baseline normalize the word and pass it to Tess. */ void Tesseract::classify_word_pass1(const WordData& word_data, WERD_RES** in_word, PointerVector* out_words) { ROW* row = word_data.row; BLOCK* block = word_data.block; prev_word_best_choice_ = word_data.prev_word != NULL ? word_data.prev_word->word->best_choice : NULL; #ifndef NO_CUBE_BUILD // If we only intend to run cube - run it and return. if (tessedit_ocr_engine_mode == OEM_CUBE_ONLY) { cube_word_pass1(block, row, *in_word); return; } #endif WERD_RES* word = *in_word; 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 && !word->IsAmbiguous()) tess_add_doc_word(word->best_choice); } } // Helper to report the result of the xheight fix. void Tesseract::ReportXhtFixResult(bool accept_new_word, float new_x_ht, WERD_RES* word, WERD_RES* new_word) { tprintf("New XHT Match:%s = %s ", word->best_choice->unichar_string().string(), word->best_choice->debug_string().string()); word->reject_map.print(debug_fp); tprintf(" -> %s = %s ", new_word->best_choice->unichar_string().string(), new_word->best_choice->debug_string().string()); new_word->reject_map.print(debug_fp); tprintf(" %s->%s %s %s\n", word->guessed_x_ht ? "GUESS" : "CERT", new_word->guessed_x_ht ? "GUESS" : "CERT", new_x_ht > 0.1 ? "STILL DOUBT" : "OK", accept_new_word ? "ACCEPTED" : ""); } // Run the x-height fix-up, based on min/max top/bottom information in // unicharset. // Returns true if the word was changed. // See the comment in fixxht.cpp for a description of the overall process. bool Tesseract::TrainedXheightFix(WERD_RES *word, BLOCK* block, ROW *row) { int original_misfits = CountMisfitTops(word); if (original_misfits == 0) return false; float baseline_shift = 0.0f; float new_x_ht = ComputeCompatibleXheight(word, &baseline_shift); if (baseline_shift != 0.0f) { // Try the shift on its own first. if (!TestNewNormalization(original_misfits, baseline_shift, word->x_height, word, block, row)) return false; original_misfits = CountMisfitTops(word); if (original_misfits > 0) { float new_baseline_shift; // Now recompute the new x_height. new_x_ht = ComputeCompatibleXheight(word, &new_baseline_shift); if (new_x_ht >= kMinRefitXHeightFraction * word->x_height) { // No test of return value here, as we are definitely making a change // to the word by shifting the baseline. TestNewNormalization(original_misfits, baseline_shift, new_x_ht, word, block, row); } } return true; } else if (new_x_ht >= kMinRefitXHeightFraction * word->x_height) { return TestNewNormalization(original_misfits, 0.0f, new_x_ht, word, block, row); } else { return false; } } // Runs recognition with the test baseline shift and x-height and returns true // if there was an improvement in recognition result. bool Tesseract::TestNewNormalization(int original_misfits, float baseline_shift, float new_x_ht, WERD_RES *word, BLOCK* block, ROW *row) { bool accept_new_x_ht = false; WERD_RES new_x_ht_word(word->word); if (word->blamer_bundle != NULL) { new_x_ht_word.blamer_bundle = new BlamerBundle(); new_x_ht_word.blamer_bundle->CopyTruth(*(word->blamer_bundle)); } new_x_ht_word.x_height = new_x_ht; new_x_ht_word.baseline_shift = baseline_shift; new_x_ht_word.caps_height = 0.0; new_x_ht_word.SetupForRecognition( unicharset, this, BestPix(), tessedit_ocr_engine_mode, NULL, classify_bln_numeric_mode, textord_use_cjk_fp_model, poly_allow_detailed_fx, 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) { tprintf("Old misfits=%d with x-height %f, new=%d with x-height %f\n", original_misfits, word->x_height, new_misfits, new_x_ht); tprintf("Old rating= %f, certainty=%f, new=%f, %f\n", word->best_choice->rating(), word->best_choice->certainty(), new_x_ht_word.best_choice->rating(), new_x_ht_word.best_choice->certainty()); } // The misfits must improve and either the rating or certainty. accept_new_x_ht = new_misfits < original_misfits && (new_x_ht_word.best_choice->certainty() > word->best_choice->certainty() || new_x_ht_word.best_choice->rating() < word->best_choice->rating()); if (debug_x_ht_level >= 1) { ReportXhtFixResult(accept_new_x_ht, new_x_ht, word, &new_x_ht_word); } } if (accept_new_x_ht) { word->ConsumeWordResults(&new_x_ht_word); return true; } return false; } /** * classify_word_pass2 * * Control what to do with the word in pass 2 */ void Tesseract::classify_word_pass2(const WordData& word_data, WERD_RES** in_word, PointerVector* out_words) { // Return if we do not want to run Tesseract. if (tessedit_ocr_engine_mode != OEM_TESSERACT_ONLY && tessedit_ocr_engine_mode != OEM_TESSERACT_CUBE_COMBINED && word_data.word->best_choice != NULL) return; if (tessedit_ocr_engine_mode == OEM_CUBE_ONLY) { return; } ROW* row = word_data.row; BLOCK* block = word_data.block; WERD_RES* word = *in_word; prev_word_best_choice_ = word_data.prev_word != NULL ? word_data.prev_word->word->best_choice : NULL; set_global_subloc_code(SUBLOC_NORM); check_debug_pt(word, 30); if (!word->done) { word->caps_height = 0.0; if (word->x_height == 0.0f) word->x_height = row->x_height(); 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)) { if (unicharset.top_bottom_useful() && unicharset.script_has_xheight() && block->classify_rotation().y() == 0.0f) { // Use the tops and bottoms since they are available. TrainedXheightFix(word, block, row); } set_global_subloc_code(SUBLOC_NORM); } #ifndef GRAPHICS_DISABLED if (tessedit_display_outwords) { if (fx_win == NULL) create_fx_win(); clear_fx_win(); word->rebuild_word->plot(fx_win); TBOX wbox = word->rebuild_word->bounding_box(); fx_win->ZoomToRectangle(wbox.left(), wbox.top(), wbox.right(), wbox.bottom()); ScrollView::Update(); } #endif set_global_subloc_code(SUBLOC_NORM); check_debug_pt(word, 50); } /** * match_word_pass2 * * Baseline normalize the word and pass it to Tess. */ void Tesseract::match_word_pass_n(int pass_n, WERD_RES *word, ROW *row, BLOCK* block) { if (word->tess_failed) return; tess_segment_pass_n(pass_n, word); if (!word->tess_failed) { if (!word->word->flag (W_REP_CHAR)) { word->fix_quotes(); if (tessedit_fix_hyphens) word->fix_hyphens(); /* Don't trust fix_quotes! - though I think I've fixed the bug */ if (word->best_choice->length() != word->box_word->length()) { tprintf("POST FIX_QUOTES FAIL String:\"%s\"; Strlen=%d;" " #Blobs=%d\n", word->best_choice->debug_string().string(), word->best_choice->length(), word->box_word->length()); } word->tess_accepted = tess_acceptable_word(word); // Also sets word->done flag make_reject_map(word, row, pass_n); } } set_word_fonts(word); ASSERT_HOST(word->raw_choice != NULL); } // Helper to return the best rated BLOB_CHOICE in the whole word that matches // the given char_id, or NULL if none can be found. 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; 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; } } return best_choice; } // Helper to insert blob_choice in each location in the leader word if there is // no matching BLOB_CHOICE there already, and correct any incorrect results // in the best_choice. static void CorrectRepcharChoices(BLOB_CHOICE* blob_choice, WERD_RES* word_res) { WERD_CHOICE* word = word_res->best_choice; for (int i = 0; i < word_res->best_choice->length(); ++i) { BLOB_CHOICE* choice = FindMatchingChoice(blob_choice->unichar_id(), word_res->GetBlobChoices(i)); if (choice == NULL) { BLOB_CHOICE_IT choice_it(word_res->GetBlobChoices(i)); choice_it.add_before_stay_put(new BLOB_CHOICE(*blob_choice)); } } // Correct any incorrect results in word. for (int i = 0; i < word->length(); ++i) { if (word->unichar_id(i) != blob_choice->unichar_id()) word->set_unichar_id(blob_choice->unichar_id(), i); } } /** * fix_rep_char() * The word is a repeated char. (Leader.) Find the repeated char character. * Create the appropriate single-word or multi-word sequence according to * the size of spaces in between blobs, and correct the classifications * where some of the characters disagree with the majority. */ void Tesseract::fix_rep_char(PAGE_RES_IT* page_res_it) { WERD_RES *word_res = page_res_it->word(); const WERD_CHOICE &word = *(word_res->best_choice); // Find the frequency of each unique character in the word. SortHelper rep_ch(word.length()); for (int i = 0; i < word.length(); ++i) { rep_ch.Add(word.unichar_id(i), 1); } // Find the most frequent result. UNICHAR_ID maxch_id = INVALID_UNICHAR_ID; // most common char int max_count = rep_ch.MaxCount(&maxch_id); // Find the best exemplar of a classifier result for maxch_id. BLOB_CHOICE* best_choice = FindBestMatchingChoice(maxch_id, word_res); if (best_choice == NULL) { tprintf("Failed to find a choice for %s, occurring %d times\n", word_res->uch_set->debug_str(maxch_id).string(), max_count); return; } word_res->done = TRUE; // Measure the mean space. int gap_count = 0; WERD* werd = word_res->word; C_BLOB_IT blob_it(werd->cblob_list()); C_BLOB* prev_blob = blob_it.data(); for (blob_it.forward(); !blob_it.at_first(); blob_it.forward()) { C_BLOB* blob = blob_it.data(); int gap = blob->bounding_box().left(); gap -= prev_blob->bounding_box().right(); ++gap_count; prev_blob = blob; } // Just correct existing classification. CorrectRepcharChoices(best_choice, word_res); word_res->reject_map.initialise(word.length()); } ACCEPTABLE_WERD_TYPE Tesseract::acceptable_word_string( const UNICHARSET& char_set, const char *s, const char *lengths) { int i = 0; int offset = 0; int leading_punct_count; int upper_count = 0; int hyphen_pos = -1; ACCEPTABLE_WERD_TYPE word_type = AC_UNACCEPTABLE; if (strlen (lengths) > 20) return word_type; /* Single Leading punctuation char*/ if (s[offset] != '\0' && STRING(chs_leading_punct).contains(s[offset])) offset += lengths[i++]; leading_punct_count = i; /* Initial cap */ while (s[offset] != '\0' && char_set.get_isupper(s + offset, lengths[i])) { offset += lengths[i++]; upper_count++; } if (upper_count > 1) { word_type = AC_UPPER_CASE; } else { /* Lower case word, possibly with an initial cap */ while (s[offset] != '\0' && char_set.get_islower(s + offset, lengths[i])) { offset += lengths[i++]; } if (i - leading_punct_count < quality_min_initial_alphas_reqd) goto not_a_word; /* Allow a single hyphen in a lower case word - don't trust upper case - I've seen several cases of "H" -> "I-I" */ if (lengths[i] == 1 && s[offset] == '-') { hyphen_pos = i; offset += lengths[i++]; if (s[offset] != '\0') { while ((s[offset] != '\0') && char_set.get_islower(s + offset, lengths[i])) { offset += lengths[i++]; } if (i < hyphen_pos + 3) goto not_a_word; } } else { /* Allow "'s" in NON hyphenated lower case words */ if (lengths[i] == 1 && (s[offset] == '\'') && lengths[i + 1] == 1 && (s[offset + lengths[i]] == 's')) { offset += lengths[i++]; offset += lengths[i++]; } } if (upper_count > 0) word_type = AC_INITIAL_CAP; else word_type = AC_LOWER_CASE; } /* Up to two different, constrained trailing punctuation chars */ if (lengths[i] == 1 && s[offset] != '\0' && STRING(chs_trailing_punct1).contains(s[offset])) offset += lengths[i++]; if (lengths[i] == 1 && s[offset] != '\0' && i > 0 && s[offset - lengths[i - 1]] != s[offset] && STRING(chs_trailing_punct2).contains (s[offset])) offset += lengths[i++]; if (s[offset] != '\0') word_type = AC_UNACCEPTABLE; not_a_word: if (word_type == AC_UNACCEPTABLE) { /* Look for abbreviation string */ i = 0; offset = 0; if (s[0] != '\0' && char_set.get_isupper(s, lengths[0])) { word_type = AC_UC_ABBREV; while (s[offset] != '\0' && char_set.get_isupper(s + offset, lengths[i]) && lengths[i + 1] == 1 && s[offset + lengths[i]] == '.') { offset += lengths[i++]; offset += lengths[i++]; } } else if (s[0] != '\0' && char_set.get_islower(s, lengths[0])) { word_type = AC_LC_ABBREV; while (s[offset] != '\0' && char_set.get_islower(s + offset, lengths[i]) && lengths[i + 1] == 1 && s[offset + lengths[i]] == '.') { offset += lengths[i++]; offset += lengths[i++]; } } if (s[offset] != '\0') word_type = AC_UNACCEPTABLE; } return word_type; } BOOL8 Tesseract::check_debug_pt(WERD_RES *word, int location) { BOOL8 show_map_detail = FALSE; inT16 i; if (!test_pt) return FALSE; tessedit_rejection_debug.set_value (FALSE); debug_x_ht_level.set_value(0); if (word->word->bounding_box ().contains (FCOORD (test_pt_x, test_pt_y))) { if (location < 0) return TRUE; // For breakpoint use tessedit_rejection_debug.set_value (TRUE); debug_x_ht_level.set_value(2); tprintf ("\n\nTESTWD::"); switch (location) { case 0: tprintf ("classify_word_pass1 start\n"); word->word->print(); break; case 10: tprintf ("make_reject_map: initial map"); break; case 20: tprintf ("make_reject_map: after NN"); break; case 30: tprintf ("classify_word_pass2 - START"); break; case 40: tprintf ("classify_word_pass2 - Pre Xht"); break; case 50: tprintf ("classify_word_pass2 - END"); show_map_detail = TRUE; break; case 60: tprintf ("fixspace"); break; case 70: tprintf ("MM pass START"); break; case 80: tprintf ("MM pass END"); break; case 90: tprintf ("After Poor quality rejection"); break; case 100: tprintf ("unrej_good_quality_words - START"); break; case 110: tprintf ("unrej_good_quality_words - END"); break; case 120: tprintf ("Write results pass"); show_map_detail = TRUE; break; } if (word->best_choice != NULL) { tprintf(" \"%s\" ", word->best_choice->unichar_string().string()); word->reject_map.print(debug_fp); tprintf("\n"); if (show_map_detail) { tprintf("\"%s\"\n", word->best_choice->unichar_string().string()); for (i = 0; word->best_choice->unichar_string()[i] != '\0'; i++) { tprintf("**** \"%c\" ****\n", word->best_choice->unichar_string()[i]); word->reject_map[i].full_print(debug_fp); } } } else { tprintf("null best choice\n"); } tprintf ("Tess Accepted: %s\n", word->tess_accepted ? "TRUE" : "FALSE"); tprintf ("Done flag: %s\n\n", word->done ? "TRUE" : "FALSE"); return TRUE; } else { return FALSE; } } /** * find_modal_font * * Find the modal font and remove from the stats. */ static void find_modal_font( //good chars in word STATS *fonts, //font stats inT16 *font_out, //output font inT8 *font_count //output count ) { inT16 font; //font index inT32 count; //pile couat if (fonts->get_total () > 0) { font = (inT16) fonts->mode (); *font_out = font; count = fonts->pile_count (font); *font_count = count < MAX_INT8 ? count : MAX_INT8; fonts->add (font, -*font_count); } else { *font_out = -1; *font_count = 0; } } /** * set_word_fonts * * Get the fonts for the word. */ 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); int fontinfo_size = get_fontinfo_table().size(); if (fontinfo_size == 0) return; GenericVector font_total_score; font_total_score.init_to_size(fontinfo_size, 0); word->italic = 0; word->bold = 0; // Compute the font scores for the word if (tessedit_debug_fonts) { tprintf("Examining fonts in %s\n", word->best_choice->debug_string().string()); } for (int b = 0; b < word->best_choice->length(); ++b) { BLOB_CHOICE* choice = word->GetBlobChoice(b); if (choice == NULL) continue; const GenericVector& fonts = choice->fonts(); for (int f = 0; f < fonts.size(); ++f) { int fontinfo_id = fonts[f].fontinfo_id; if (0 <= fontinfo_id && fontinfo_id < fontinfo_size) { font_total_score[fontinfo_id] += fonts[f].score; } } } // Find the top and 2nd choice for the word. int score1 = 0, score2 = 0; inT16 font_id1 = -1, font_id2 = -1; for (int f = 0; f < fontinfo_size; ++f) { if (tessedit_debug_fonts && font_total_score[f] > 0) { tprintf("Font %s, total score = %d\n", fontinfo_table_.get(f).name, font_total_score[f]); } if (font_total_score[f] > score1) { score2 = score1; font_id2 = font_id1; score1 = font_total_score[f]; font_id1 = f; } else if (font_total_score[f] > score2) { score2 = font_total_score[f]; font_id2 = f; } } word->fontinfo = font_id1 >= 0 ? &fontinfo_table_.get(font_id1) : NULL; word->fontinfo2 = font_id2 >= 0 ? &fontinfo_table_.get(font_id2) : NULL; // Each score has a limit of MAX_UINT16, so divide by that to get the number // of "votes" for that font, ie number of perfect scores. word->fontinfo_id_count = ClipToRange(score1 / MAX_UINT16, 1, MAX_INT8); word->fontinfo_id2_count = ClipToRange(score2 / MAX_UINT16, 0, MAX_INT8); if (score1 > 0) { FontInfo fi = fontinfo_table_.get(font_id1); if (tessedit_debug_fonts) { if (word->fontinfo_id2_count > 0) { tprintf("Word modal font=%s, score=%d, 2nd choice %s/%d\n", fi.name, word->fontinfo_id_count, fontinfo_table_.get(font_id2).name, word->fontinfo_id2_count); } else { tprintf("Word modal font=%s, score=%d. No 2nd choice\n", fi.name, word->fontinfo_id_count); } } word->italic = (fi.is_italic() ? 1 : -1) * word->fontinfo_id_count; word->bold = (fi.is_bold() ? 1 : -1) * word->fontinfo_id_count; } } /** * font_recognition_pass * * Smooth the fonts for the document. */ void Tesseract::font_recognition_pass(PAGE_RES* page_res) { PAGE_RES_IT page_res_it(page_res); WERD_RES *word; // current word STATS doc_fonts(0, font_table_size_); // font counters // Gather font id statistics. for (page_res_it.restart_page(); page_res_it.word() != NULL; page_res_it.forward()) { word = page_res_it.word(); if (word->fontinfo != NULL) { doc_fonts.add(word->fontinfo->universal_id, word->fontinfo_id_count); } if (word->fontinfo2 != NULL) { doc_fonts.add(word->fontinfo2->universal_id, word->fontinfo_id2_count); } } inT16 doc_font; // modal font inT8 doc_font_count; // modal font find_modal_font(&doc_fonts, &doc_font, &doc_font_count); if (doc_font_count == 0) return; // Get the modal font pointer. const FontInfo* modal_font = NULL; for (page_res_it.restart_page(); page_res_it.word() != NULL; page_res_it.forward()) { word = page_res_it.word(); if (word->fontinfo != NULL && word->fontinfo->universal_id == doc_font) { modal_font = word->fontinfo; break; } if (word->fontinfo2 != NULL && word->fontinfo2->universal_id == doc_font) { modal_font = word->fontinfo2; break; } } ASSERT_HOST(modal_font != NULL); // Assign modal font to weak words. for (page_res_it.restart_page(); page_res_it.word() != NULL; page_res_it.forward()) { word = page_res_it.word(); int length = word->best_choice->length(); int count = word->fontinfo_id_count; if (!(count == length || (length > 3 && count >= length * 3 / 4))) { word->fontinfo = modal_font; // Counts only get 1 as it came from the doc. word->fontinfo_id_count = 1; word->italic = modal_font->is_italic() ? 1 : -1; word->bold = modal_font->is_bold() ? 1 : -1; } } } // If a word has multiple alternates check if the best choice is in the // dictionary. If not, replace it with an alternate that exists in the // dictionary. void Tesseract::dictionary_correction_pass(PAGE_RES *page_res) { PAGE_RES_IT word_it(page_res); for (WERD_RES* word = word_it.word(); word != NULL; word = word_it.forward()) { if (word->best_choices.singleton()) continue; // There are no alternates. WERD_CHOICE* best = word->best_choice; if (word->tesseract->getDict().valid_word(*best) != 0) continue; // The best choice is in the dictionary. WERD_CHOICE_IT choice_it(&word->best_choices); for (choice_it.mark_cycle_pt(); !choice_it.cycled_list(); choice_it.forward()) { WERD_CHOICE* alternate = choice_it.data(); if (word->tesseract->getDict().valid_word(*alternate)) { // The alternate choice is in the dictionary. if (tessedit_bigram_debug) { tprintf("Dictionary correction replaces best choice '%s' with '%s'\n", best->unichar_string().string(), alternate->unichar_string().string()); } // Replace the 'best' choice with a better choice. word->ReplaceBestChoice(alternate); break; } } } } } // namespace tesseract tesseract-3.04.01/ccmain/control.h000066400000000000000000000030071266071204500167420ustar00rootroot00000000000000/********************************************************************** * File: control.h (Formerly control.h) * Description: Module-independent matcher controller. * Author: Ray Smith * Created: Thu Apr 23 11:09:58 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. * **********************************************************************/ /** * @file control.h * Module-independent matcher controller. */ #ifndef CONTROL_H #define CONTROL_H #include "params.h" #include "ocrblock.h" #include "ratngs.h" #include "statistc.h" #include "pageres.h" enum ACCEPTABLE_WERD_TYPE { AC_UNACCEPTABLE, ///< Unacceptable word AC_LOWER_CASE, ///< ALL lower case AC_UPPER_CASE, ///< ALL upper case AC_INITIAL_CAP, ///< ALL but initial lc AC_LC_ABBREV, ///< a.b.c. AC_UC_ABBREV ///< A.B.C. }; #endif tesseract-3.04.01/ccmain/cube_control.cpp000066400000000000000000000357001266071204500203000ustar00rootroot00000000000000/****************************************************************** * File: cube_control.cpp * Description: Tesseract class methods for invoking cube convolutional * neural network word recognizer. * Author: Raquel Romano * Created: September 2009 * **********************************************************************/ // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "allheaders.h" #include "cube_object.h" #include "cube_reco_context.h" #include "tesseractclass.h" #include "tesseract_cube_combiner.h" namespace tesseract { /** * @name convert_prob_to_tess_certainty * * Normalize a probability in the range [0.0, 1.0] to a tesseract * certainty in the range [-20.0, 0.0] */ static float convert_prob_to_tess_certainty(float prob) { return (prob - 1.0) * 20.0; } /** * @name char_box_to_tbox * * Create a TBOX from a character bounding box. If nonzero, the * x_offset accounts for any additional padding of the word box that * should be taken into account. * */ TBOX char_box_to_tbox(Box* char_box, TBOX word_box, int x_offset) { l_int32 left; l_int32 top; l_int32 width; l_int32 height; l_int32 right; l_int32 bottom; boxGetGeometry(char_box, &left, &top, &width, &height); left += word_box.left() - x_offset; right = left + width; top = word_box.bottom() + word_box.height() - top; bottom = top - height; return TBOX(left, bottom, right, top); } /** * @name extract_cube_state * * Extract CharSamp objects and character bounding boxes from the * CubeObject's state. The caller should free both structres. * */ bool Tesseract::extract_cube_state(CubeObject* cube_obj, int* num_chars, Boxa** char_boxes, CharSamp*** char_samples) { if (!cube_obj) { if (cube_debug_level > 0) { tprintf("Cube WARNING (extract_cube_state): Invalid cube object " "passed to extract_cube_state\n"); } return false; } // Note that the CubeObject accessors return either the deslanted or // regular objects search object or beam search object, whichever // was used in the last call to Recognize() CubeSearchObject* cube_search_obj = cube_obj->SrchObj(); if (!cube_search_obj) { if (cube_debug_level > 0) { tprintf("Cube WARNING (Extract_cube_state): Could not retrieve " "cube's search object in extract_cube_state.\n"); } return false; } BeamSearch *beam_search_obj = cube_obj->BeamObj(); if (!beam_search_obj) { if (cube_debug_level > 0) { tprintf("Cube WARNING (Extract_cube_state): Could not retrieve " "cube's beam search object in extract_cube_state.\n"); } return false; } // Get the character samples and bounding boxes by backtracking // through the beam search path int best_node_index = beam_search_obj->BestPresortedNodeIndex(); *char_samples = beam_search_obj->BackTrack( cube_search_obj, best_node_index, num_chars, NULL, char_boxes); if (!*char_samples) return false; return true; } /** * @name create_cube_box_word * * Fill the given BoxWord with boxes from character bounding * boxes. The char_boxes have local coordinates w.r.t. the * word bounding box, i.e., the left-most character bbox of each word * has (0,0) left-top coord, but the BoxWord must be defined in page * coordinates. */ bool Tesseract::create_cube_box_word(Boxa *char_boxes, int num_chars, TBOX word_box, BoxWord* box_word) { if (!box_word) { if (cube_debug_level > 0) { tprintf("Cube WARNING (create_cube_box_word): Invalid box_word.\n"); } return false; } // Find the x-coordinate of left-most char_box, which could be // nonzero if the word image was padded before recognition took place. int x_offset = -1; for (int i = 0; i < num_chars; ++i) { Box* char_box = boxaGetBox(char_boxes, i, L_CLONE); if (x_offset < 0 || char_box->x < x_offset) { x_offset = char_box->x; } boxDestroy(&char_box); } for (int i = 0; i < num_chars; ++i) { Box* char_box = boxaGetBox(char_boxes, i, L_CLONE); TBOX tbox = char_box_to_tbox(char_box, word_box, x_offset); boxDestroy(&char_box); box_word->InsertBox(i, tbox); } return true; } /** * @name init_cube_objects * * Instantiates Tesseract object's CubeRecoContext and TesseractCubeCombiner. * Returns false if cube context could not be created or if load_combiner is * true, but the combiner could not be loaded. */ bool Tesseract::init_cube_objects(bool load_combiner, TessdataManager *tessdata_manager) { ASSERT_HOST(cube_cntxt_ == NULL); ASSERT_HOST(tess_cube_combiner_ == NULL); // Create the cube context object cube_cntxt_ = CubeRecoContext::Create(this, tessdata_manager, &unicharset); if (cube_cntxt_ == NULL) { if (cube_debug_level > 0) { tprintf("Cube WARNING (Tesseract::init_cube_objects()): Failed to " "instantiate CubeRecoContext\n"); } return false; } // Create the combiner object and load the combiner net for target languages. if (load_combiner) { tess_cube_combiner_ = new tesseract::TesseractCubeCombiner(cube_cntxt_); if (!tess_cube_combiner_ || !tess_cube_combiner_->LoadCombinerNet()) { delete cube_cntxt_; cube_cntxt_ = NULL; if (tess_cube_combiner_ != NULL) { delete tess_cube_combiner_; tess_cube_combiner_ = NULL; } if (cube_debug_level > 0) tprintf("Cube ERROR (Failed to instantiate TesseractCubeCombiner\n"); return false; } } return true; } /** * @name run_cube_combiner * * Iterates through tesseract's results and calls cube on each word, * combining the results with the existing tesseract result. */ void Tesseract::run_cube_combiner(PAGE_RES *page_res) { if (page_res == NULL || tess_cube_combiner_ == NULL) return; PAGE_RES_IT page_res_it(page_res); // Iterate through the word results and call cube on each word. for (page_res_it.restart_page(); page_res_it.word () != NULL; page_res_it.forward()) { BLOCK* block = page_res_it.block()->block; if (block->poly_block() != NULL && !block->poly_block()->IsText()) continue; // Don't deal with non-text blocks. WERD_RES* word = page_res_it.word(); // Skip cube entirely if tesseract's certainty is greater than threshold. int combiner_run_thresh = convert_prob_to_tess_certainty( cube_cntxt_->Params()->CombinerRunThresh()); if (word->best_choice->certainty() >= combiner_run_thresh) { continue; } // Use the same language as Tesseract used for the word. Tesseract* lang_tess = word->tesseract; // Setup a trial WERD_RES in which to classify with cube. WERD_RES cube_word; cube_word.InitForRetryRecognition(*word); cube_word.SetupForRecognition(lang_tess->unicharset, this, BestPix(), OEM_CUBE_ONLY, NULL, false, false, false, page_res_it.row()->row, page_res_it.block()->block); CubeObject *cube_obj = lang_tess->cube_recognize_word( page_res_it.block()->block, &cube_word); if (cube_obj != NULL) lang_tess->cube_combine_word(cube_obj, &cube_word, word); delete cube_obj; } } /** * @name cube_word_pass1 * * Recognizes a single word using (only) cube. Compatible with * Tesseract's classify_word_pass1/classify_word_pass2. */ void Tesseract::cube_word_pass1(BLOCK* block, ROW *row, WERD_RES *word) { CubeObject *cube_obj = cube_recognize_word(block, word); delete cube_obj; } /** * @name cube_recognize_word * * Cube recognizer to recognize a single word as with classify_word_pass1 * but also returns the cube object in case the combiner is needed. */ CubeObject* Tesseract::cube_recognize_word(BLOCK* block, WERD_RES* word) { if (!cube_binary_ || !cube_cntxt_) { if (cube_debug_level > 0 && !cube_binary_) tprintf("Tesseract::run_cube(): NULL binary image.\n"); word->SetupFake(unicharset); return NULL; } TBOX word_box = word->word->bounding_box(); if (block != NULL && (block->re_rotation().x() != 1.0f || block->re_rotation().y() != 0.0f)) { // TODO(rays) We have to rotate the bounding box to get the true coords. // This will be achieved in the future via DENORM. // In the mean time, cube can't process this word. if (cube_debug_level > 0) { tprintf("Cube can't process rotated word at:"); word_box.print(); } word->SetupFake(unicharset); return NULL; } CubeObject* cube_obj = new tesseract::CubeObject( cube_cntxt_, cube_binary_, word_box.left(), pixGetHeight(cube_binary_) - word_box.top(), word_box.width(), word_box.height()); if (!cube_recognize(cube_obj, block, word)) { delete cube_obj; return NULL; } return cube_obj; } /** * @name cube_combine_word * * Combines the cube and tesseract results for a single word, leaving the * result in tess_word. */ void Tesseract::cube_combine_word(CubeObject* cube_obj, WERD_RES* cube_word, WERD_RES* tess_word) { float combiner_prob = tess_cube_combiner_->CombineResults(tess_word, cube_obj); // If combiner probability is greater than tess/cube combiner // classifier threshold, i.e. tesseract wins, then just return the // tesseract result unchanged, as the combiner knows nothing about how // correct the answer is. If cube and tesseract agree, then improve the // scores before returning. WERD_CHOICE* tess_best = tess_word->best_choice; WERD_CHOICE* cube_best = cube_word->best_choice; if (cube_debug_level || classify_debug_level) { tprintf("Combiner prob = %g vs threshold %g\n", combiner_prob, cube_cntxt_->Params()->CombinerClassifierThresh()); } if (combiner_prob >= cube_cntxt_->Params()->CombinerClassifierThresh()) { if (tess_best->unichar_string() == cube_best->unichar_string()) { // Cube and tess agree, so improve the scores. tess_best->set_rating(tess_best->rating() / 2); tess_best->set_certainty(tess_best->certainty() / 2); } return; } // Cube wins. // It is better for the language combiner to have all tesseract scores, // so put them in the cube result. cube_best->set_rating(tess_best->rating()); cube_best->set_certainty(tess_best->certainty()); if (cube_debug_level || classify_debug_level) { tprintf("Cube INFO: tesseract result replaced by cube: %s -> %s\n", tess_best->unichar_string().string(), cube_best->unichar_string().string()); } tess_word->ConsumeWordResults(cube_word); } /** * @name cube_recognize * * Call cube on the current word, and write the result to word. * Sets up a fake result and returns false if something goes wrong. */ bool Tesseract::cube_recognize(CubeObject *cube_obj, BLOCK* block, WERD_RES *word) { // Run cube WordAltList *cube_alt_list = cube_obj->RecognizeWord(); if (!cube_alt_list || cube_alt_list->AltCount() <= 0) { if (cube_debug_level > 0) { tprintf("Cube returned nothing for word at:"); word->word->bounding_box().print(); } word->SetupFake(unicharset); return false; } // Get cube's best result and its probability, mapped to tesseract's // certainty range char_32 *cube_best_32 = cube_alt_list->Alt(0); double cube_prob = CubeUtils::Cost2Prob(cube_alt_list->AltCost(0)); float cube_certainty = convert_prob_to_tess_certainty(cube_prob); string cube_best_str; CubeUtils::UTF32ToUTF8(cube_best_32, &cube_best_str); // Retrieve Cube's character bounding boxes and CharSamples, // corresponding to the most recent call to RecognizeWord(). Boxa *char_boxes = NULL; CharSamp **char_samples = NULL;; int num_chars; if (!extract_cube_state(cube_obj, &num_chars, &char_boxes, &char_samples) && cube_debug_level > 0) { tprintf("Cube WARNING (Tesseract::cube_recognize): Cannot extract " "cube state.\n"); word->SetupFake(unicharset); return false; } // Convert cube's character bounding boxes to a BoxWord. BoxWord cube_box_word; TBOX tess_word_box = word->word->bounding_box(); if (word->denorm.block() != NULL) tess_word_box.rotate(word->denorm.block()->re_rotation()); bool box_word_success = create_cube_box_word(char_boxes, num_chars, tess_word_box, &cube_box_word); boxaDestroy(&char_boxes); if (!box_word_success) { if (cube_debug_level > 0) { tprintf("Cube WARNING (Tesseract::cube_recognize): Could not " "create cube BoxWord\n"); } word->SetupFake(unicharset); return false; } // Fill tesseract result's fields with cube results fill_werd_res(cube_box_word, cube_best_str.c_str(), word); // 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, -cube_certainty, cube_certainty, -1, 0.0f, 0.0f, 0.0f, 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", word->best_choice->unichar_string().string(), word->best_choice->rating(), word->best_choice->certainty()); } return true; } /** * @name fill_werd_res * * Fill Tesseract's word result fields with cube's. * */ void Tesseract::fill_werd_res(const BoxWord& cube_box_word, const char* cube_best_str, WERD_RES* tess_werd_res) { 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(), tess_werd_res->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); // 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; // Set word to done, i.e., ignore all of tesseract's tests for rejection tess_werd_res->done = tess_werd_res->tess_accepted; } } // namespace tesseract tesseract-3.04.01/ccmain/cube_reco_context.cpp000066400000000000000000000151511266071204500213120ustar00rootroot00000000000000/********************************************************************** * File: cube_reco_context.cpp * Description: Implementation of the Cube Recognition Context Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 #include "cube_reco_context.h" #include "classifier_factory.h" #include "cube_tuning_params.h" #include "dict.h" #include "feature_bmp.h" #include "tessdatamanager.h" #include "tesseractclass.h" #include "tess_lang_model.h" namespace tesseract { /** * Instantiate a CubeRecoContext object using a Tesseract object. * CubeRecoContext will not take ownership of tess_obj, but will * record the pointer to it and will make use of various Tesseract * components (language model, flags, etc). Thus the caller should * keep tess_obj alive so long as the instantiated CubeRecoContext is used. */ CubeRecoContext::CubeRecoContext(Tesseract *tess_obj) { tess_obj_ = tess_obj; lang_ = ""; loaded_ = false; lang_mod_ = NULL; params_ = NULL; char_classifier_ = NULL; char_set_ = NULL; word_size_model_ = NULL; char_bigrams_ = NULL; word_unigrams_ = NULL; noisy_input_ = false; size_normalization_ = false; } CubeRecoContext::~CubeRecoContext() { if (char_classifier_ != NULL) { delete char_classifier_; char_classifier_ = NULL; } if (word_size_model_ != NULL) { delete word_size_model_; word_size_model_ = NULL; } if (char_set_ != NULL) { delete char_set_; char_set_ = NULL; } if (char_bigrams_ != NULL) { delete char_bigrams_; char_bigrams_ = NULL; } if (word_unigrams_ != NULL) { delete word_unigrams_; word_unigrams_ = NULL; } if (lang_mod_ != NULL) { delete lang_mod_; lang_mod_ = NULL; } if (params_ != NULL) { delete params_; params_ = NULL; } } /** * Returns the path of the data files by looking up the TESSDATA_PREFIX * environment variable and appending a "tessdata" directory to it */ bool CubeRecoContext::GetDataFilePath(string *path) const { *path = tess_obj_->datadir.string(); return true; } /** * The object initialization function that loads all the necessary * components of a RecoContext. TessdataManager is used to load the * data from [lang].traineddata file. If TESSDATA_CUBE_UNICHARSET * component is present, Cube will be instantiated with the unicharset * specified in this component and the corresponding dictionary * (TESSDATA_CUBE_SYSTEM_DAWG), and will map Cube's unicharset to * Tesseract's. Otherwise, TessdataManager will assume that Cube will * be using Tesseract's unicharset and dawgs, and will load the * unicharset from the TESSDATA_UNICHARSET component and will load the * dawgs from TESSDATA_*_DAWG components. */ bool CubeRecoContext::Load(TessdataManager *tessdata_manager, UNICHARSET *tess_unicharset) { ASSERT_HOST(tess_obj_ != NULL); tess_unicharset_ = tess_unicharset; string data_file_path; // Get the data file path. if (GetDataFilePath(&data_file_path) == false) { fprintf(stderr, "Unable to get data file path\n"); return false; } // Get the language from the Tesseract object. lang_ = tess_obj_->lang.string(); // Create the char set. if ((char_set_ = CharSet::Create(tessdata_manager, tess_unicharset)) == NULL) { fprintf(stderr, "Cube ERROR (CubeRecoContext::Load): unable to load " "CharSet\n"); return false; } // Create the language model. string lm_file_name = data_file_path + lang_ + ".cube.lm"; string lm_params; if (!CubeUtils::ReadFileToString(lm_file_name, &lm_params)) { fprintf(stderr, "Cube ERROR (CubeRecoContext::Load): unable to read cube " "language model params from %s\n", lm_file_name.c_str()); return false; } lang_mod_ = new TessLangModel(lm_params, data_file_path, tess_obj_->getDict().load_system_dawg, tessdata_manager, this); if (lang_mod_ == NULL) { fprintf(stderr, "Cube ERROR (CubeRecoContext::Load): unable to create " "TessLangModel\n"); return false; } // Create the optional char bigrams object. char_bigrams_ = CharBigrams::Create(data_file_path, lang_); // Create the optional word unigrams object. word_unigrams_ = WordUnigrams::Create(data_file_path, lang_); // Create the optional size model. word_size_model_ = WordSizeModel::Create(data_file_path, lang_, char_set_, Contextual()); // Load tuning params. params_ = CubeTuningParams::Create(data_file_path, lang_); if (params_ == NULL) { fprintf(stderr, "Cube ERROR (CubeRecoContext::Load): unable to read " "CubeTuningParams from %s\n", data_file_path.c_str()); return false; } // Create the char classifier. char_classifier_ = CharClassifierFactory::Create(data_file_path, lang_, lang_mod_, char_set_, params_); if (char_classifier_ == NULL) { fprintf(stderr, "Cube ERROR (CubeRecoContext::Load): unable to load " "CharClassifierFactory object from %s\n", data_file_path.c_str()); return false; } loaded_ = true; return true; } /** Creates a CubeRecoContext object using a tesseract object */ CubeRecoContext * CubeRecoContext::Create(Tesseract *tess_obj, TessdataManager *tessdata_manager, UNICHARSET *tess_unicharset) { // create the object CubeRecoContext *cntxt = new CubeRecoContext(tess_obj); if (cntxt == NULL) { fprintf(stderr, "Cube ERROR (CubeRecoContext::Create): unable to create " "CubeRecoContext object\n"); return NULL; } // load the necessary components if (cntxt->Load(tessdata_manager, tess_unicharset) == false) { fprintf(stderr, "Cube ERROR (CubeRecoContext::Create): unable to init " "CubeRecoContext object\n"); delete cntxt; return NULL; } // success return cntxt; } } // tesseract} tesseract-3.04.01/ccmain/cube_reco_context.h000066400000000000000000000121641266071204500207600ustar00rootroot00000000000000/********************************************************************** * File: cube_reco_context.h * Description: Declaration of the Cube Recognition Context Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The CubeRecoContext class abstracts the Cube OCR Engine. Typically a process // (or a thread) would create one CubeRecoContext object per language. // The CubeRecoContext object also provides methods to get and set the // different attribues of the Cube OCR Engine. #ifndef CUBE_RECO_CONTEXT_H #define CUBE_RECO_CONTEXT_H #include #include "neural_net.h" #include "lang_model.h" #include "classifier_base.h" #include "feature_base.h" #include "char_set.h" #include "word_size_model.h" #include "char_bigrams.h" #include "word_unigrams.h" namespace tesseract { class Tesseract; class TessdataManager; class CubeRecoContext { public: // Reading order enum type enum ReadOrder { L2R, R2L }; // Instantiate using a Tesseract object CubeRecoContext(Tesseract *tess_obj); ~CubeRecoContext(); // accessor functions inline const string & Lang() const { return lang_; } inline CharSet *CharacterSet() const { return char_set_; } const UNICHARSET *TessUnicharset() const { return tess_unicharset_; } inline CharClassifier *Classifier() const { return char_classifier_; } inline WordSizeModel *SizeModel() const { return word_size_model_; } inline CharBigrams *Bigrams() const { return char_bigrams_; } inline WordUnigrams *WordUnigramsObj() const { return word_unigrams_; } inline TuningParams *Params() const { return params_; } inline LangModel *LangMod() const { return lang_mod_; } // the reading order of the language inline ReadOrder ReadingOrder() const { return ((lang_ == "ara") ? R2L : L2R); } // does the language support case inline bool HasCase() const { return (lang_ != "ara" && lang_ != "hin"); } inline bool Cursive() const { return (lang_ == "ara"); } inline bool HasItalics() const { return (lang_ != "ara" && lang_ != "hin"); } inline bool Contextual() const { return (lang_ == "ara"); } // RecoContext runtime flags accessor functions inline bool SizeNormalization() const { return size_normalization_; } inline bool NoisyInput() const { return noisy_input_; } inline bool OOD() const { return lang_mod_->OOD(); } inline bool Numeric() const { return lang_mod_->Numeric(); } inline bool WordList() const { return lang_mod_->WordList(); } inline bool Punc() const { return lang_mod_->Punc(); } inline bool CaseSensitive() const { return char_classifier_->CaseSensitive(); } inline void SetSizeNormalization(bool size_normalization) { size_normalization_ = size_normalization; } inline void SetNoisyInput(bool noisy_input) { noisy_input_ = noisy_input; } inline void SetOOD(bool ood_enabled) { lang_mod_->SetOOD(ood_enabled); } inline void SetNumeric(bool numeric_enabled) { lang_mod_->SetNumeric(numeric_enabled); } inline void SetWordList(bool word_list_enabled) { lang_mod_->SetWordList(word_list_enabled); } inline void SetPunc(bool punc_enabled) { lang_mod_->SetPunc(punc_enabled); } inline void SetCaseSensitive(bool case_sensitive) { char_classifier_->SetCaseSensitive(case_sensitive); } inline tesseract::Tesseract *TesseractObject() const { return tess_obj_; } // Returns the path of the data files bool GetDataFilePath(string *path) const; // Creates a CubeRecoContext object using a tesseract object. Data // files are loaded via the tessdata_manager, and the tesseract // unicharset is provided in order to map Cube's unicharset to // Tesseract's in the case where the two unicharsets differ. static CubeRecoContext *Create(Tesseract *tess_obj, TessdataManager *tessdata_manager, UNICHARSET *tess_unicharset); private: bool loaded_; string lang_; CharSet *char_set_; UNICHARSET *tess_unicharset_; WordSizeModel *word_size_model_; CharClassifier *char_classifier_; CharBigrams *char_bigrams_; WordUnigrams *word_unigrams_; TuningParams *params_; LangModel *lang_mod_; Tesseract *tess_obj_; // CubeRecoContext does not own this pointer bool size_normalization_; bool noisy_input_; // Loads and initialized all the necessary components of a // CubeRecoContext. See .cpp for more details. bool Load(TessdataManager *tessdata_manager, UNICHARSET *tess_unicharset); }; } #endif // CUBE_RECO_CONTEXT_H tesseract-3.04.01/ccmain/cubeclassifier.cpp000066400000000000000000000113271266071204500206040ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: cubeclassifier.cpp // Description: Cube implementation of a ShapeClassifier. // Author: Ray Smith // Created: Wed Nov 23 10:39:45 PST 2011 // // (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. // /////////////////////////////////////////////////////////////////////// #include "cubeclassifier.h" #include "char_altlist.h" #include "char_set.h" #include "cube_object.h" #include "cube_reco_context.h" #include "tessclassifier.h" #include "tesseractclass.h" #include "trainingsample.h" #include "unicharset.h" namespace tesseract { CubeClassifier::CubeClassifier(tesseract::Tesseract* tesseract) : cube_cntxt_(tesseract->GetCubeRecoContext()), shape_table_(*tesseract->shape_table()) { } CubeClassifier::~CubeClassifier() { } /// Classifies the given [training] sample, writing to results. /// See ShapeClassifier for a full description. int CubeClassifier::UnicharClassifySample( const TrainingSample& sample, Pix* page_pix, int debug, UNICHAR_ID keep_this, GenericVector* results) { results->clear(); if (page_pix == NULL) return 0; ASSERT_HOST(cube_cntxt_ != NULL); const TBOX& char_box = sample.bounding_box(); CubeObject* cube_obj = new tesseract::CubeObject( cube_cntxt_, page_pix, char_box.left(), pixGetHeight(page_pix) - char_box.top(), char_box.width(), char_box.height()); CharAltList* alt_list = cube_obj->RecognizeChar(); if (alt_list != NULL) { alt_list->Sort(); CharSet* char_set = cube_cntxt_->CharacterSet(); for (int i = 0; i < alt_list->AltCount(); ++i) { // Convert cube representation to a shape_id. int alt_id = alt_list->Alt(i); int unichar_id = char_set->UnicharID(char_set->ClassString(alt_id)); if (unichar_id >= 0) results->push_back(UnicharRating(unichar_id, alt_list->AltProb(i))); } delete alt_list; } delete cube_obj; return results->size(); } /** Provides access to the ShapeTable that this classifier works with. */ const ShapeTable* CubeClassifier::GetShapeTable() const { return &shape_table_; } CubeTessClassifier::CubeTessClassifier(tesseract::Tesseract* tesseract) : cube_cntxt_(tesseract->GetCubeRecoContext()), shape_table_(*tesseract->shape_table()), pruner_(new TessClassifier(true, tesseract)) { } CubeTessClassifier::~CubeTessClassifier() { delete pruner_; } /// Classifies the given [training] sample, writing to results. /// See ShapeClassifier for a full description. int CubeTessClassifier::UnicharClassifySample( const TrainingSample& sample, Pix* page_pix, int debug, UNICHAR_ID keep_this, GenericVector* results) { int num_results = pruner_->UnicharClassifySample(sample, page_pix, debug, keep_this, results); if (page_pix == NULL) return num_results; ASSERT_HOST(cube_cntxt_ != NULL); const TBOX& char_box = sample.bounding_box(); CubeObject* cube_obj = new tesseract::CubeObject( cube_cntxt_, page_pix, char_box.left(), pixGetHeight(page_pix) - char_box.top(), char_box.width(), char_box.height()); CharAltList* alt_list = cube_obj->RecognizeChar(); CharSet* char_set = cube_cntxt_->CharacterSet(); if (alt_list != NULL) { for (int r = 0; r < num_results; ++r) { // Get the best cube probability of the unichar in the result. double best_prob = 0.0; for (int i = 0; i < alt_list->AltCount(); ++i) { int alt_id = alt_list->Alt(i); int unichar_id = char_set->UnicharID(char_set->ClassString(alt_id)); if (unichar_id == (*results)[r].unichar_id && alt_list->AltProb(i) > best_prob) { best_prob = alt_list->AltProb(i); } } (*results)[r].rating = best_prob; } delete alt_list; // Re-sort by rating. results->sort(&UnicharRating::SortDescendingRating); } delete cube_obj; return results->size(); } /** Provides access to the ShapeTable that this classifier works with. */ const ShapeTable* CubeTessClassifier::GetShapeTable() const { return &shape_table_; } } // namespace tesseract tesseract-3.04.01/ccmain/cubeclassifier.h000066400000000000000000000055501266071204500202520ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: cubeclassifier.h // Description: Cube implementation of a ShapeClassifier. // Author: Ray Smith // Created: Wed Nov 23 10:36:32 PST 2011 // // (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 THIRD_PARTY_TESSERACT_CCMAIN_CUBECLASSIFIER_H_ #define THIRD_PARTY_TESSERACT_CCMAIN_CUBECLASSIFIER_H_ #include "shapeclassifier.h" namespace tesseract { class Classify; class CubeRecoContext; class ShapeTable; class TessClassifier; class Tesseract; class TrainingSample; struct UnicharRating; // Cube implementation of a ShapeClassifier. class CubeClassifier : public ShapeClassifier { public: explicit CubeClassifier(Tesseract* tesseract); virtual ~CubeClassifier(); // Classifies the given [training] sample, writing to results. // See ShapeClassifier for a full description. virtual int UnicharClassifySample(const TrainingSample& sample, Pix* page_pix, int debug, UNICHAR_ID keep_this, GenericVector* results); // Provides access to the ShapeTable that this classifier works with. virtual const ShapeTable* GetShapeTable() const; private: // Cube objects. CubeRecoContext* cube_cntxt_; const ShapeTable& shape_table_; }; // Combination of Tesseract class pruner with scoring by cube. class CubeTessClassifier : public ShapeClassifier { public: explicit CubeTessClassifier(Tesseract* tesseract); virtual ~CubeTessClassifier(); // Classifies the given [training] sample, writing to results. // See ShapeClassifier for a full description. virtual int UnicharClassifySample(const TrainingSample& sample, Pix* page_pix, int debug, UNICHAR_ID keep_this, GenericVector* results); // Provides access to the ShapeTable that this classifier works with. virtual const ShapeTable* GetShapeTable() const; private: // Cube objects. CubeRecoContext* cube_cntxt_; const ShapeTable& shape_table_; TessClassifier* pruner_; }; } // namespace tesseract #endif /* THIRD_PARTY_TESSERACT_CCMAIN_CUBECLASSIFIER_H_ */ tesseract-3.04.01/ccmain/docqual.cpp000066400000000000000000001026561266071204500172570ustar00rootroot00000000000000/****************************************************************** * File: docqual.cpp (Formerly docqual.c) * Description: Document Quality Metrics * Author: Phil Cheatle * Created: Mon May 9 11:27:28 BST 1994 * * (C) Copyright 1994, 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. * **********************************************************************/ #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #endif #include #include "docqual.h" #include "reject.h" #include "tesscallback.h" #include "tessvars.h" #include "globals.h" #include "tesseractclass.h" namespace tesseract{ // A little class to provide the callbacks as we have no pre-bound args. struct DocQualCallbacks { explicit DocQualCallbacks(WERD_RES* word0) : word(word0), match_count(0), accepted_match_count(0) {} void CountMatchingBlobs(int index) { ++match_count; } void CountAcceptedBlobs(int index) { if (word->reject_map[index].accepted()) ++accepted_match_count; ++match_count; } void AcceptIfGoodQuality(int index) { if (word->reject_map[index].accept_if_good_quality()) word->reject_map[index].setrej_quality_accept(); } WERD_RES* word; inT16 match_count; inT16 accepted_match_count; }; /************************************************************************* * word_blob_quality() * How many blobs in the box_word are identical to those of the inword? * ASSUME blobs in both initial word and box_word are in ascending order of * left hand blob edge. *************************************************************************/ inT16 Tesseract::word_blob_quality(WERD_RES *word, ROW *row) { if (word->bln_boxes == NULL || word->rebuild_word == NULL || word->rebuild_word->blobs.empty()) return 0; DocQualCallbacks cb(word); word->bln_boxes->ProcessMatchedBlobs( *word->rebuild_word, NewPermanentTessCallback(&cb, &DocQualCallbacks::CountMatchingBlobs)); return cb.match_count; } inT16 Tesseract::word_outline_errs(WERD_RES *word) { inT16 i = 0; inT16 err_count = 0; if (word->rebuild_word != NULL) { 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++; } } return err_count; } /************************************************************************* * word_char_quality() * Combination of blob quality and outline quality - how many good chars are * there? - I.e chars which pass the blob AND outline tests. *************************************************************************/ void Tesseract::word_char_quality(WERD_RES *word, ROW *row, inT16 *match_count, inT16 *accepted_match_count) { if (word->bln_boxes == NULL || word->rebuild_word == NULL || word->rebuild_word->blobs.empty()) return; DocQualCallbacks cb(word); word->bln_boxes->ProcessMatchedBlobs( *word->rebuild_word, NewPermanentTessCallback(&cb, &DocQualCallbacks::CountAcceptedBlobs)); *match_count = cb.match_count; *accepted_match_count = cb.accepted_match_count; } /************************************************************************* * unrej_good_chs() * Unreject POTENTIAL rejects if the blob passes the blob and outline checks *************************************************************************/ void Tesseract::unrej_good_chs(WERD_RES *word, ROW *row) { if (word->bln_boxes == NULL || word->rebuild_word == NULL || word->rebuild_word->blobs.empty()) return; DocQualCallbacks cb(word); word->bln_boxes->ProcessMatchedBlobs( *word->rebuild_word, NewPermanentTessCallback(&cb, &DocQualCallbacks::AcceptIfGoodQuality)); } inT16 Tesseract::count_outline_errs(char c, inT16 outline_count) { int expected_outline_count; if (STRING (outlines_odd).contains (c)) return 0; //Don't use this char else if (STRING (outlines_2).contains (c)) expected_outline_count = 2; else expected_outline_count = 1; return abs (outline_count - expected_outline_count); } void Tesseract::quality_based_rejection(PAGE_RES_IT &page_res_it, BOOL8 good_quality_doc) { if ((tessedit_good_quality_unrej && good_quality_doc)) unrej_good_quality_words(page_res_it); doc_and_block_rejection(page_res_it, good_quality_doc); if (unlv_tilde_crunching) { tilde_crunch(page_res_it); tilde_delete(page_res_it); } } /************************************************************************* * unrej_good_quality_words() * Accept potential rejects in words which pass the following checks: * - Contains a potential reject * - Word looks like a sensible alpha word. * - Word segmentation is the same as the original image * - All characters have the expected number of outlines * NOTE - the rejection counts are recalculated after unrejection * - CAN'T do it in a single pass without a bit of fiddling * - keep it simple but inefficient *************************************************************************/ void Tesseract::unrej_good_quality_words( //unreject potential PAGE_RES_IT &page_res_it) { WERD_RES *word; ROW_RES *current_row; BLOCK_RES *current_block; int i; page_res_it.restart_page (); while (page_res_it.word () != NULL) { check_debug_pt (page_res_it.word (), 100); if (bland_unrej) { word = page_res_it.word (); for (i = 0; i < word->reject_map.length (); i++) { if (word->reject_map[i].accept_if_good_quality ()) word->reject_map[i].setrej_quality_accept (); } page_res_it.forward (); } else if ((page_res_it.row ()->char_count > 0) && ((page_res_it.row ()->rej_count / (float) page_res_it.row ()->char_count) <= quality_rowrej_pc)) { word = page_res_it.word (); if (word->reject_map.quality_recoverable_rejects() && (tessedit_unrej_any_wd || acceptable_word_string(*word->uch_set, word->best_choice->unichar_string().string(), word->best_choice->unichar_lengths().string()) != AC_UNACCEPTABLE)) { unrej_good_chs(word, page_res_it.row ()->row); } page_res_it.forward (); } else { /* Skip to end of dodgy row */ current_row = page_res_it.row (); while ((page_res_it.word () != NULL) && (page_res_it.row () == current_row)) page_res_it.forward (); } check_debug_pt (page_res_it.word (), 110); } page_res_it.restart_page (); page_res_it.page_res->char_count = 0; page_res_it.page_res->rej_count = 0; current_block = NULL; current_row = NULL; while (page_res_it.word () != NULL) { if (current_block != page_res_it.block ()) { current_block = page_res_it.block (); current_block->char_count = 0; current_block->rej_count = 0; } if (current_row != page_res_it.row ()) { current_row = page_res_it.row (); current_row->char_count = 0; current_row->rej_count = 0; current_row->whole_word_rej_count = 0; } page_res_it.rej_stat_word (); page_res_it.forward (); } } /************************************************************************* * doc_and_block_rejection() * * If the page has too many rejects - reject all of it. * If any block has too many rejects - reject all words in the block *************************************************************************/ void Tesseract::doc_and_block_rejection( //reject big chunks PAGE_RES_IT &page_res_it, BOOL8 good_quality_doc) { inT16 block_no = 0; inT16 row_no = 0; BLOCK_RES *current_block; ROW_RES *current_row; BOOL8 rej_word; BOOL8 prev_word_rejected; inT16 char_quality = 0; inT16 accepted_char_quality; if (page_res_it.page_res->rej_count * 100.0 / page_res_it.page_res->char_count > tessedit_reject_doc_percent) { reject_whole_page(page_res_it); if (tessedit_debug_doc_rejection) { tprintf("REJECT ALL #chars: %d #Rejects: %d; \n", page_res_it.page_res->char_count, page_res_it.page_res->rej_count); } } else { if (tessedit_debug_doc_rejection) { tprintf("NO PAGE REJECTION #chars: %d # Rejects: %d; \n", page_res_it.page_res->char_count, page_res_it.page_res->rej_count); } /* Walk blocks testing for block rejection */ page_res_it.restart_page(); WERD_RES* word; while ((word = page_res_it.word()) != NULL) { current_block = page_res_it.block(); block_no = current_block->block->index(); if (current_block->char_count > 0 && (current_block->rej_count * 100.0 / current_block->char_count) > tessedit_reject_block_percent) { if (tessedit_debug_block_rejection) { tprintf("REJECTING BLOCK %d #chars: %d; #Rejects: %d\n", block_no, current_block->char_count, current_block->rej_count); } prev_word_rejected = FALSE; while ((word = page_res_it.word()) != NULL && (page_res_it.block() == current_block)) { if (tessedit_preserve_blk_rej_perfect_wds) { rej_word = word->reject_map.reject_count() > 0 || word->reject_map.length () < tessedit_preserve_min_wd_len; if (rej_word && tessedit_dont_blkrej_good_wds && word->reject_map.length() >= tessedit_preserve_min_wd_len && acceptable_word_string( *word->uch_set, word->best_choice->unichar_string().string(), word->best_choice->unichar_lengths().string()) != AC_UNACCEPTABLE) { word_char_quality(word, page_res_it.row()->row, &char_quality, &accepted_char_quality); rej_word = char_quality != word->reject_map.length(); } } else { rej_word = TRUE; } if (rej_word) { /* Reject spacing if both current and prev words are rejected. NOTE - this is NOT restricted to FUZZY spaces. - When tried this generated more space errors. */ if (tessedit_use_reject_spaces && prev_word_rejected && page_res_it.prev_row() == page_res_it.row() && word->word->space() == 1) word->reject_spaces = TRUE; word->reject_map.rej_word_block_rej(); } prev_word_rejected = rej_word; page_res_it.forward(); } } else { if (tessedit_debug_block_rejection) { tprintf("NOT REJECTING BLOCK %d #chars: %d # Rejects: %d; \n", block_no, page_res_it.block()->char_count, page_res_it.block()->rej_count); } /* Walk rows in block testing for row rejection */ row_no = 0; while (page_res_it.word() != NULL && page_res_it.block() == current_block) { current_row = page_res_it.row(); row_no++; /* Reject whole row if: fraction of chars on row which are rejected exceed a limit AND fraction rejects which occur in WHOLE WERD rejects is LESS THAN a limit */ if (current_row->char_count > 0 && (current_row->rej_count * 100.0 / current_row->char_count) > tessedit_reject_row_percent && (current_row->whole_word_rej_count * 100.0 / current_row->rej_count) < tessedit_whole_wd_rej_row_percent) { if (tessedit_debug_block_rejection) { tprintf("REJECTING ROW %d #chars: %d; #Rejects: %d\n", row_no, current_row->char_count, current_row->rej_count); } prev_word_rejected = FALSE; while ((word = page_res_it.word()) != NULL && page_res_it.row () == current_row) { /* Preserve words on good docs unless they are mostly rejected*/ if (!tessedit_row_rej_good_docs && good_quality_doc) { rej_word = word->reject_map.reject_count() / static_cast(word->reject_map.length()) > tessedit_good_doc_still_rowrej_wd; } else if (tessedit_preserve_row_rej_perfect_wds) { /* Preserve perfect words anyway */ rej_word = word->reject_map.reject_count() > 0 || word->reject_map.length () < tessedit_preserve_min_wd_len; if (rej_word && tessedit_dont_rowrej_good_wds && word->reject_map.length() >= tessedit_preserve_min_wd_len && acceptable_word_string(*word->uch_set, word->best_choice->unichar_string().string(), word->best_choice->unichar_lengths().string()) != AC_UNACCEPTABLE) { word_char_quality(word, page_res_it.row()->row, &char_quality, &accepted_char_quality); rej_word = char_quality != word->reject_map.length(); } } else { rej_word = TRUE; } if (rej_word) { /* Reject spacing if both current and prev words are rejected. NOTE - this is NOT restricted to FUZZY spaces. - When tried this generated more space errors. */ if (tessedit_use_reject_spaces && prev_word_rejected && page_res_it.prev_row() == page_res_it.row() && word->word->space () == 1) word->reject_spaces = TRUE; word->reject_map.rej_word_row_rej(); } prev_word_rejected = rej_word; page_res_it.forward(); } } else { if (tessedit_debug_block_rejection) { tprintf("NOT REJECTING ROW %d #chars: %d # Rejects: %d; \n", row_no, current_row->char_count, current_row->rej_count); } while (page_res_it.word() != NULL && page_res_it.row() == current_row) page_res_it.forward(); } } } } } } } // namespace tesseract /************************************************************************* * reject_whole_page() * Don't believe any of it - set the reject map to 00..00 in all words * *************************************************************************/ void reject_whole_page(PAGE_RES_IT &page_res_it) { page_res_it.restart_page (); while (page_res_it.word () != NULL) { page_res_it.word ()->reject_map.rej_word_doc_rej (); page_res_it.forward (); } //whole page is rejected page_res_it.page_res->rejected = TRUE; } namespace tesseract { void Tesseract::tilde_crunch(PAGE_RES_IT &page_res_it) { WERD_RES *word; GARBAGE_LEVEL garbage_level; PAGE_RES_IT copy_it; BOOL8 prev_potential_marked = FALSE; BOOL8 found_terrible_word = FALSE; BOOL8 ok_dict_word; page_res_it.restart_page(); while (page_res_it.word() != NULL) { POLY_BLOCK* pb = page_res_it.block()->block->poly_block(); if (pb != NULL && !pb->IsText()) { page_res_it.forward(); continue; } word = page_res_it.word(); if (crunch_early_convert_bad_unlv_chs) convert_bad_unlv_chs(word); if (crunch_early_merge_tess_fails) word->merge_tess_fails(); if (word->reject_map.accept_count () != 0) { found_terrible_word = FALSE; //Forget earlier potential crunches prev_potential_marked = FALSE; } else { ok_dict_word = safe_dict_word(word); garbage_level = garbage_word (word, ok_dict_word); if ((garbage_level != G_NEVER_CRUNCH) && (terrible_word_crunch (word, garbage_level))) { if (crunch_debug > 0) { tprintf ("T CRUNCHING: \"%s\"\n", word->best_choice->unichar_string().string()); } word->unlv_crunch_mode = CR_KEEP_SPACE; if (prev_potential_marked) { while (copy_it.word () != word) { if (crunch_debug > 0) { tprintf ("P1 CRUNCHING: \"%s\"\n", copy_it.word()->best_choice->unichar_string().string()); } copy_it.word ()->unlv_crunch_mode = CR_KEEP_SPACE; copy_it.forward (); } prev_potential_marked = FALSE; } found_terrible_word = TRUE; } else if ((garbage_level != G_NEVER_CRUNCH) && (potential_word_crunch (word, garbage_level, ok_dict_word))) { if (found_terrible_word) { if (crunch_debug > 0) { tprintf ("P2 CRUNCHING: \"%s\"\n", word->best_choice->unichar_string().string()); } word->unlv_crunch_mode = CR_KEEP_SPACE; } else if (!prev_potential_marked) { copy_it = page_res_it; prev_potential_marked = TRUE; if (crunch_debug > 1) { tprintf ("P3 CRUNCHING: \"%s\"\n", word->best_choice->unichar_string().string()); } } } else { found_terrible_word = FALSE; //Forget earlier potential crunches prev_potential_marked = FALSE; if (crunch_debug > 2) { tprintf ("NO CRUNCH: \"%s\"\n", word->best_choice->unichar_string().string()); } } } page_res_it.forward (); } } BOOL8 Tesseract::terrible_word_crunch(WERD_RES *word, GARBAGE_LEVEL garbage_level) { float rating_per_ch; int adjusted_len; int crunch_mode = 0; if ((word->best_choice->unichar_string().length () == 0) || (strspn (word->best_choice->unichar_string().string(), " ") == word->best_choice->unichar_string().length ())) crunch_mode = 1; else { adjusted_len = word->reject_map.length (); if (adjusted_len > crunch_rating_max) adjusted_len = crunch_rating_max; rating_per_ch = word->best_choice->rating () / adjusted_len; if (rating_per_ch > crunch_terrible_rating) crunch_mode = 2; else if (crunch_terrible_garbage && (garbage_level == G_TERRIBLE)) crunch_mode = 3; else if ((word->best_choice->certainty () < crunch_poor_garbage_cert) && (garbage_level != G_OK)) crunch_mode = 4; else if ((rating_per_ch > crunch_poor_garbage_rate) && (garbage_level != G_OK)) crunch_mode = 5; } if (crunch_mode > 0) { if (crunch_debug > 2) { tprintf ("Terrible_word_crunch (%d) on \"%s\"\n", crunch_mode, word->best_choice->unichar_string().string()); } return TRUE; } else return FALSE; } BOOL8 Tesseract::potential_word_crunch(WERD_RES *word, GARBAGE_LEVEL garbage_level, BOOL8 ok_dict_word) { float rating_per_ch; int adjusted_len; const char *str = word->best_choice->unichar_string().string(); const char *lengths = word->best_choice->unichar_lengths().string(); BOOL8 word_crunchable; int poor_indicator_count = 0; word_crunchable = !crunch_leave_accept_strings || word->reject_map.length() < 3 || (acceptable_word_string(*word->uch_set, str, lengths) == AC_UNACCEPTABLE && !ok_dict_word); adjusted_len = word->reject_map.length(); if (adjusted_len > 10) adjusted_len = 10; rating_per_ch = word->best_choice->rating() / adjusted_len; if (rating_per_ch > crunch_pot_poor_rate) { if (crunch_debug > 2) { tprintf("Potential poor rating on \"%s\"\n", word->best_choice->unichar_string().string()); } poor_indicator_count++; } if (word_crunchable && word->best_choice->certainty() < crunch_pot_poor_cert) { if (crunch_debug > 2) { tprintf("Potential poor cert on \"%s\"\n", word->best_choice->unichar_string().string()); } poor_indicator_count++; } if (garbage_level != G_OK) { if (crunch_debug > 2) { tprintf("Potential garbage on \"%s\"\n", word->best_choice->unichar_string().string()); } poor_indicator_count++; } return poor_indicator_count >= crunch_pot_indicators; } void Tesseract::tilde_delete(PAGE_RES_IT &page_res_it) { WERD_RES *word; PAGE_RES_IT copy_it; BOOL8 deleting_from_bol = FALSE; BOOL8 marked_delete_point = FALSE; inT16 debug_delete_mode; CRUNCH_MODE delete_mode; inT16 x_debug_delete_mode; CRUNCH_MODE x_delete_mode; page_res_it.restart_page(); while (page_res_it.word() != NULL) { word = page_res_it.word(); delete_mode = word_deletable (word, debug_delete_mode); if (delete_mode != CR_NONE) { if (word->word->flag (W_BOL) || deleting_from_bol) { if (crunch_debug > 0) { tprintf ("BOL CRUNCH DELETING(%d): \"%s\"\n", debug_delete_mode, word->best_choice->unichar_string().string()); } word->unlv_crunch_mode = delete_mode; deleting_from_bol = TRUE; } else if (word->word->flag(W_EOL)) { if (marked_delete_point) { while (copy_it.word() != word) { x_delete_mode = word_deletable (copy_it.word (), x_debug_delete_mode); if (crunch_debug > 0) { tprintf ("EOL CRUNCH DELETING(%d): \"%s\"\n", x_debug_delete_mode, copy_it.word()->best_choice->unichar_string().string()); } copy_it.word ()->unlv_crunch_mode = x_delete_mode; copy_it.forward (); } } if (crunch_debug > 0) { tprintf ("EOL CRUNCH DELETING(%d): \"%s\"\n", debug_delete_mode, word->best_choice->unichar_string().string()); } word->unlv_crunch_mode = delete_mode; deleting_from_bol = FALSE; marked_delete_point = FALSE; } else { if (!marked_delete_point) { copy_it = page_res_it; marked_delete_point = TRUE; } } } else { deleting_from_bol = FALSE; //Forget earlier potential crunches marked_delete_point = FALSE; } /* The following step has been left till now as the tess fails are used to determine if the word is deletable. */ if (!crunch_early_merge_tess_fails) word->merge_tess_fails(); page_res_it.forward (); } } void Tesseract::convert_bad_unlv_chs(WERD_RES *word_res) { int i; UNICHAR_ID unichar_dash = word_res->uch_set->unichar_to_id("-"); UNICHAR_ID unichar_space = word_res->uch_set->unichar_to_id(" "); UNICHAR_ID unichar_tilde = word_res->uch_set->unichar_to_id("~"); UNICHAR_ID unichar_pow = word_res->uch_set->unichar_to_id("^"); for (i = 0; i < word_res->reject_map.length(); ++i) { if (word_res->best_choice->unichar_id(i) == unichar_tilde) { word_res->best_choice->set_unichar_id(unichar_dash, i); if (word_res->reject_map[i].accepted ()) word_res->reject_map[i].setrej_unlv_rej (); } if (word_res->best_choice->unichar_id(i) == unichar_pow) { word_res->best_choice->set_unichar_id(unichar_space, i); if (word_res->reject_map[i].accepted ()) word_res->reject_map[i].setrej_unlv_rej (); } } } GARBAGE_LEVEL Tesseract::garbage_word(WERD_RES *word, BOOL8 ok_dict_word) { enum STATES { JUNK, FIRST_UPPER, FIRST_LOWER, FIRST_NUM, SUBSEQUENT_UPPER, SUBSEQUENT_LOWER, SUBSEQUENT_NUM }; const char *str = word->best_choice->unichar_string().string(); const char *lengths = word->best_choice->unichar_lengths().string(); STATES state = JUNK; int len = 0; int isolated_digits = 0; int isolated_alphas = 0; int bad_char_count = 0; int tess_rejs = 0; int dodgy_chars = 0; int ok_chars; UNICHAR_ID last_char = -1; int alpha_repetition_count = 0; int longest_alpha_repetition_count = 0; int longest_lower_run_len = 0; int lower_string_count = 0; int longest_upper_run_len = 0; int upper_string_count = 0; int total_alpha_count = 0; int total_digit_count = 0; for (; *str != '\0'; str += *(lengths++)) { len++; if (word->uch_set->get_isupper (str, *lengths)) { total_alpha_count++; switch (state) { case SUBSEQUENT_UPPER: case FIRST_UPPER: state = SUBSEQUENT_UPPER; upper_string_count++; if (longest_upper_run_len < upper_string_count) longest_upper_run_len = upper_string_count; if (last_char == word->uch_set->unichar_to_id(str, *lengths)) { alpha_repetition_count++; if (longest_alpha_repetition_count < alpha_repetition_count) { longest_alpha_repetition_count = alpha_repetition_count; } } else { last_char = word->uch_set->unichar_to_id(str, *lengths); alpha_repetition_count = 1; } break; case FIRST_NUM: isolated_digits++; default: state = FIRST_UPPER; last_char = word->uch_set->unichar_to_id(str, *lengths); alpha_repetition_count = 1; upper_string_count = 1; break; } } else if (word->uch_set->get_islower (str, *lengths)) { total_alpha_count++; switch (state) { case SUBSEQUENT_LOWER: case FIRST_LOWER: state = SUBSEQUENT_LOWER; lower_string_count++; if (longest_lower_run_len < lower_string_count) longest_lower_run_len = lower_string_count; if (last_char == word->uch_set->unichar_to_id(str, *lengths)) { alpha_repetition_count++; if (longest_alpha_repetition_count < alpha_repetition_count) { longest_alpha_repetition_count = alpha_repetition_count; } } else { last_char = word->uch_set->unichar_to_id(str, *lengths); alpha_repetition_count = 1; } break; case FIRST_NUM: isolated_digits++; default: state = FIRST_LOWER; last_char = word->uch_set->unichar_to_id(str, *lengths); alpha_repetition_count = 1; lower_string_count = 1; break; } } else if (word->uch_set->get_isdigit (str, *lengths)) { total_digit_count++; switch (state) { case FIRST_NUM: state = SUBSEQUENT_NUM; case SUBSEQUENT_NUM: break; case FIRST_UPPER: case FIRST_LOWER: isolated_alphas++; default: state = FIRST_NUM; break; } } else { if (*lengths == 1 && *str == ' ') tess_rejs++; else bad_char_count++; switch (state) { case FIRST_NUM: isolated_digits++; break; case FIRST_UPPER: case FIRST_LOWER: isolated_alphas++; default: break; } state = JUNK; } } switch (state) { case FIRST_NUM: isolated_digits++; break; case FIRST_UPPER: case FIRST_LOWER: isolated_alphas++; default: break; } if (crunch_include_numerals) { total_alpha_count += total_digit_count - isolated_digits; } if (crunch_leave_ok_strings && len >= 4 && 2 * (total_alpha_count - isolated_alphas) > len && longest_alpha_repetition_count < crunch_long_repetitions) { if ((crunch_accept_ok && acceptable_word_string(*word->uch_set, str, lengths) != AC_UNACCEPTABLE) || longest_lower_run_len > crunch_leave_lc_strings || longest_upper_run_len > crunch_leave_uc_strings) return G_NEVER_CRUNCH; } if (word->reject_map.length() > 1 && strpbrk(str, " ") == NULL && (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 || acceptable_word_string(*word->uch_set, str, lengths) != AC_UNACCEPTABLE || ok_dict_word)) return G_OK; ok_chars = len - bad_char_count - isolated_digits - isolated_alphas - tess_rejs; if (crunch_debug > 3) { tprintf("garbage_word: \"%s\"\n", word->best_choice->unichar_string().string()); tprintf("LEN: %d bad: %d iso_N: %d iso_A: %d rej: %d\n", len, bad_char_count, isolated_digits, isolated_alphas, tess_rejs); } if (bad_char_count == 0 && tess_rejs == 0 && (len > isolated_digits + isolated_alphas || len <= 2)) return G_OK; if (tess_rejs > ok_chars || (tess_rejs > 0 && (bad_char_count + tess_rejs) * 2 > len)) return G_TERRIBLE; if (len > 4) { dodgy_chars = 2 * tess_rejs + bad_char_count + isolated_digits + isolated_alphas; if (dodgy_chars > 5 || (dodgy_chars / (float) len) > 0.5) return G_DODGY; else return G_OK; } else { dodgy_chars = 2 * tess_rejs + bad_char_count; if ((len == 4 && dodgy_chars > 2) || (len == 3 && dodgy_chars > 2) || dodgy_chars >= len) return G_DODGY; else return G_OK; } } /************************************************************************* * word_deletable() * DELETE WERDS AT ENDS OF ROWS IF * Word is crunched && * ( string length = 0 OR * > 50% of chars are "|" (before merging) OR * certainty < -10 OR * rating /char > 60 OR * TOP of word is more than 0.5 xht BELOW baseline OR * BOTTOM of word is more than 0.5 xht ABOVE xht OR * length of word < 3xht OR * height of word < 0.7 xht OR * height of word > 3.0 xht OR * >75% of the outline BBs have longest dimension < 0.5xht *************************************************************************/ CRUNCH_MODE Tesseract::word_deletable(WERD_RES *word, inT16 &delete_mode) { int word_len = word->reject_map.length (); float rating_per_ch; TBOX box; //BB of word if (word->unlv_crunch_mode == CR_NONE) { delete_mode = 0; return CR_NONE; } if (word_len == 0) { delete_mode = 1; return CR_DELETE; } if (word->rebuild_word != NULL) { // Cube leaves rebuild_word NULL. box = word->rebuild_word->bounding_box(); if (box.height () < crunch_del_min_ht * kBlnXHeight) { delete_mode = 4; return CR_DELETE; } if (noise_outlines(word->rebuild_word)) { delete_mode = 5; return CR_DELETE; } } if ((failure_count (word) * 1.5) > word_len) { delete_mode = 2; return CR_LOOSE_SPACE; } if (word->best_choice->certainty () < crunch_del_cert) { delete_mode = 7; return CR_LOOSE_SPACE; } rating_per_ch = word->best_choice->rating () / word_len; if (rating_per_ch > crunch_del_rating) { delete_mode = 8; return CR_LOOSE_SPACE; } if (box.top () < kBlnBaselineOffset - crunch_del_low_word * kBlnXHeight) { delete_mode = 9; return CR_LOOSE_SPACE; } if (box.bottom () > kBlnBaselineOffset + crunch_del_high_word * kBlnXHeight) { delete_mode = 10; return CR_LOOSE_SPACE; } if (box.height () > crunch_del_max_ht * kBlnXHeight) { delete_mode = 11; return CR_LOOSE_SPACE; } if (box.width () < crunch_del_min_width * kBlnXHeight) { delete_mode = 3; return CR_LOOSE_SPACE; } delete_mode = 0; return CR_NONE; } inT16 Tesseract::failure_count(WERD_RES *word) { const char *str = word->best_choice->unichar_string().string(); int tess_rejs = 0; for (; *str != '\0'; str++) { if (*str == ' ') tess_rejs++; } return tess_rejs; } BOOL8 Tesseract::noise_outlines(TWERD *word) { TBOX box; // BB of outline inT16 outline_count = 0; inT16 small_outline_count = 0; inT16 max_dimension; float small_limit = kBlnXHeight * crunch_small_outlines_size; 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(); if (box.height() > box.width()) max_dimension = box.height(); else max_dimension = box.width(); if (max_dimension < small_limit) small_outline_count++; } } return small_outline_count >= outline_count; } } // namespace tesseract tesseract-3.04.01/ccmain/docqual.h000066400000000000000000000022251266071204500167130ustar00rootroot00000000000000/****************************************************************** * File: docqual.h (Formerly docqual.h) * Description: Document Quality Metrics * Author: Phil Cheatle * Created: Mon May 9 11:27:28 BST 1994 * * (C) Copyright 1994, 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 DOCQUAL_H #define DOCQUAL_H #include "control.h" enum GARBAGE_LEVEL { G_NEVER_CRUNCH, G_OK, G_DODGY, G_TERRIBLE }; inT16 word_blob_quality(WERD_RES *word, ROW *row); void reject_whole_page(PAGE_RES_IT &page_res_it); #endif tesseract-3.04.01/ccmain/equationdetect.cpp000066400000000000000000001462241266071204500206440ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: equationdetect.cpp // Description: Helper classes to detect equations. // Author: Zongyi (Joe) Liu (joeliu@google.com) // Created: Fri Aug 31 11:13:01 PST 2011 // // (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. // /////////////////////////////////////////////////////////////////////// #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #include #endif #ifdef __MINGW32__ #include #endif #include // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "equationdetect.h" #include "bbgrid.h" #include "classify.h" #include "colpartition.h" #include "colpartitiongrid.h" #include "colpartitionset.h" #include "helpers.h" #include "ratngs.h" #include "tesseractclass.h" // Config variables. BOOL_VAR(equationdetect_save_bi_image, false, "Save input bi image"); BOOL_VAR(equationdetect_save_spt_image, false, "Save special character image"); BOOL_VAR(equationdetect_save_seed_image, false, "Save the seed image"); BOOL_VAR(equationdetect_save_merged_image, false, "Save the merged image"); namespace tesseract { /////////////////////////////////////////////////////////////////////////// // Utility ColParition sort functions. /////////////////////////////////////////////////////////////////////////// static int SortCPByTopReverse(const void* p1, const void* p2) { const ColPartition* cp1 = *reinterpret_cast(p1); const ColPartition* cp2 = *reinterpret_cast(p2); ASSERT_HOST(cp1 != NULL && cp2 != NULL); const TBOX &box1(cp1->bounding_box()), &box2(cp2->bounding_box()); return box2.top() - box1.top(); } static int SortCPByBottom(const void* p1, const void* p2) { const ColPartition* cp1 = *reinterpret_cast(p1); const ColPartition* cp2 = *reinterpret_cast(p2); ASSERT_HOST(cp1 != NULL && cp2 != NULL); const TBOX &box1(cp1->bounding_box()), &box2(cp2->bounding_box()); return box1.bottom() - box2.bottom(); } static int SortCPByHeight(const void* p1, const void* p2) { const ColPartition* cp1 = *reinterpret_cast(p1); const ColPartition* cp2 = *reinterpret_cast(p2); ASSERT_HOST(cp1 != NULL && cp2 != NULL); const TBOX &box1(cp1->bounding_box()), &box2(cp2->bounding_box()); return box1.height() - box2.height(); } // TODO(joeliu): we may want to parameterize these constants. const float kMathDigitDensityTh1 = 0.25; const float kMathDigitDensityTh2 = 0.1; const float kMathItalicDensityTh = 0.5; const float kUnclearDensityTh = 0.25; const int kSeedBlobsCountTh = 10; const int kLeftIndentAlignmentCountTh = 1; // Returns true if PolyBlockType is of text type or equation type. inline bool IsTextOrEquationType(PolyBlockType type) { return PTIsTextType(type) || type == PT_EQUATION; } inline bool IsLeftIndented(const EquationDetect::IndentType type) { return type == EquationDetect::LEFT_INDENT || type == EquationDetect::BOTH_INDENT; } inline bool IsRightIndented(const EquationDetect::IndentType type) { return type == EquationDetect::RIGHT_INDENT || type == EquationDetect::BOTH_INDENT; } EquationDetect::EquationDetect(const char* equ_datapath, const char* equ_name) { const char* default_name = "equ"; if (equ_name == NULL) { equ_name = default_name; } equ_tesseract_ = lang_tesseract_ = NULL; resolution_ = 0; page_count_ = 0; // Construct equ_tesseract_. equ_tesseract_ = new Tesseract(); if (equ_tesseract_->init_tesseract(equ_datapath, equ_name, OEM_TESSERACT_ONLY)) { tprintf("Warning: equation region detection requested," " but %s failed to load from %s\n", equ_name, equ_datapath); delete equ_tesseract_; equ_tesseract_ = NULL; } cps_super_bbox_ = NULL; } EquationDetect::~EquationDetect() { if (equ_tesseract_) { delete (equ_tesseract_); } if (cps_super_bbox_) { delete(cps_super_bbox_); } } void EquationDetect::SetLangTesseract(Tesseract* lang_tesseract) { lang_tesseract_ = lang_tesseract; } void EquationDetect::SetResolution(const int resolution) { resolution_ = resolution; } int EquationDetect::LabelSpecialText(TO_BLOCK* to_block) { if (to_block == NULL) { tprintf("Warning: input to_block is NULL!\n"); return -1; } GenericVector blob_lists; blob_lists.push_back(&(to_block->blobs)); blob_lists.push_back(&(to_block->large_blobs)); for (int i = 0; i < blob_lists.size(); ++i) { BLOBNBOX_IT bbox_it(blob_lists[i]); for (bbox_it.mark_cycle_pt (); !bbox_it.cycled_list(); bbox_it.forward()) { bbox_it.data()->set_special_text_type(BSTT_NONE); } } return 0; } void EquationDetect::IdentifySpecialText( BLOBNBOX *blobnbox, const int height_th) { ASSERT_HOST(blobnbox != NULL); if (blobnbox->bounding_box().height() < height_th && height_th > 0) { // For small blob, we simply set to BSTT_NONE. blobnbox->set_special_text_type(BSTT_NONE); return; } BLOB_CHOICE_LIST ratings_equ, ratings_lang; C_BLOB* blob = blobnbox->cblob(); // 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(); float x_orig = (box.left() + box.right()) / 2.0f, y_orig = box.bottom(); TBLOB* normed_blob = new TBLOB(*tblob); 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); lang_tesseract_->AdaptiveClassifier(normed_blob, &ratings_lang); delete normed_blob; delete tblob; // Get the best choice from ratings_lang and rating_equ. As the choice in the // list has already been sorted by the certainty, we simply use the first // choice. BLOB_CHOICE *lang_choice = NULL, *equ_choice = NULL; if (ratings_lang.length() > 0) { BLOB_CHOICE_IT choice_it(&ratings_lang); lang_choice = choice_it.data(); } if (ratings_equ.length() > 0) { BLOB_CHOICE_IT choice_it(&ratings_equ); equ_choice = choice_it.data(); } float lang_score = lang_choice ? lang_choice->certainty() : -FLT_MAX; float equ_score = equ_choice ? equ_choice->certainty() : -FLT_MAX; const float kConfScoreTh = -5.0f, kConfDiffTh = 1.8; // The scores here are negative, so the max/min == fabs(min/max). // float ratio = fmax(lang_score, equ_score) / fmin(lang_score, equ_score); float diff = fabs(lang_score - equ_score); BlobSpecialTextType type = BSTT_NONE; // Classification. if (fmax(lang_score, equ_score) < kConfScoreTh) { // If both score are very small, then mark it as unclear. type = BSTT_UNCLEAR; } else if (diff > kConfDiffTh && equ_score > lang_score) { // If equ_score is significantly higher, then we classify this character as // math symbol. type = BSTT_MATH; } else if (lang_choice) { // For other cases: lang_score is similar or significantly higher. type = EstimateTypeForUnichar( lang_tesseract_->unicharset, lang_choice->unichar_id()); } if (type == BSTT_NONE && lang_tesseract_->get_fontinfo_table().get( lang_choice->fontinfo_id()).is_italic()) { // For text symbol, we still check if it is italic. blobnbox->set_special_text_type(BSTT_ITALIC); } else { blobnbox->set_special_text_type(type); } } BlobSpecialTextType EquationDetect::EstimateTypeForUnichar( const UNICHARSET& unicharset, const UNICHAR_ID id) const { STRING s = unicharset.id_to_unichar(id); if (unicharset.get_isalpha(id)) { return BSTT_NONE; } if (unicharset.get_ispunctuation(id)) { // Exclude some special texts that are likely to be confused as math symbol. static GenericVector ids_to_exclude; if (ids_to_exclude.empty()) { static const STRING kCharsToEx[] = {"'", "`", "\"", "\\", ",", ".", "〈", "〉", "《", "》", "ã€", "「", ""}; int i = 0; while (kCharsToEx[i] != "") { ids_to_exclude.push_back( unicharset.unichar_to_id(kCharsToEx[i++].string())); } ids_to_exclude.sort(); } return ids_to_exclude.bool_binary_search(id) ? BSTT_NONE : BSTT_MATH; } // Check if it is digit. In addition to the isdigit attribute, we also check // if this character belongs to those likely to be confused with a digit. static const STRING kDigitsChars = "|"; if (unicharset.get_isdigit(id) || (s.length() == 1 && kDigitsChars.contains(s[0]))) { return BSTT_DIGIT; } else { return BSTT_MATH; } } void EquationDetect::IdentifySpecialText() { // Set configuration for Tesseract::AdaptiveClassifier. equ_tesseract_->tess_cn_matching.set_value(true); // turn it on equ_tesseract_->tess_bn_matching.set_value(false); // Set the multiplier to zero for lang_tesseract_ to improve the accuracy. int classify_class_pruner = lang_tesseract_->classify_class_pruner_multiplier; int classify_integer_matcher = lang_tesseract_->classify_integer_matcher_multiplier; lang_tesseract_->classify_class_pruner_multiplier.set_value(0); lang_tesseract_->classify_integer_matcher_multiplier.set_value(0); ColPartitionGridSearch gsearch(part_grid_); ColPartition *part = NULL; gsearch.StartFullSearch(); while ((part = gsearch.NextFullSearch()) != NULL) { if (!IsTextOrEquationType(part->type())) { continue; } IdentifyBlobsToSkip(part); BLOBNBOX_C_IT bbox_it(part->boxes()); // Compute the height threshold. GenericVector blob_heights; for (bbox_it.mark_cycle_pt (); !bbox_it.cycled_list(); bbox_it.forward()) { if (bbox_it.data()->special_text_type() != BSTT_SKIP) { blob_heights.push_back(bbox_it.data()->bounding_box().height()); } } blob_heights.sort(); int height_th = blob_heights[blob_heights.size() / 2] / 3 * 2; for (bbox_it.mark_cycle_pt (); !bbox_it.cycled_list(); bbox_it.forward()) { if (bbox_it.data()->special_text_type() != BSTT_SKIP) { IdentifySpecialText(bbox_it.data(), height_th); } } } // Set the multiplier values back. lang_tesseract_->classify_class_pruner_multiplier.set_value( classify_class_pruner); lang_tesseract_->classify_integer_matcher_multiplier.set_value( classify_integer_matcher); if (equationdetect_save_spt_image) { // For debug. STRING outfile; GetOutputTiffName("_spt", &outfile); PaintSpecialTexts(outfile); } } void EquationDetect::IdentifyBlobsToSkip(ColPartition* part) { ASSERT_HOST(part); BLOBNBOX_C_IT blob_it(part->boxes()); for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { // At this moment, no blob should have been joined. ASSERT_HOST(!blob_it.data()->joined_to_prev()); } for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { BLOBNBOX* blob = blob_it.data(); if (blob->joined_to_prev() || blob->special_text_type() == BSTT_SKIP) { continue; } TBOX blob_box = blob->bounding_box(); // Search if any blob can be merged into blob. If found, then we mark all // these blobs as BSTT_SKIP. BLOBNBOX_C_IT blob_it2 = blob_it; bool found = false; while (!blob_it2.at_last()) { BLOBNBOX* nextblob = blob_it2.forward(); const TBOX& nextblob_box = nextblob->bounding_box(); if (nextblob_box.left() >= blob_box.right()) { break; } const float kWidthR = 0.4, kHeightR = 0.3; bool xoverlap = blob_box.major_x_overlap(nextblob_box), yoverlap = blob_box.y_overlap(nextblob_box); float widthR = static_cast( MIN(nextblob_box.width(), blob_box.width())) / MAX(nextblob_box.width(), blob_box.width()); float heightR = static_cast( MIN(nextblob_box.height(), blob_box.height())) / MAX(nextblob_box.height(), blob_box.height()); if (xoverlap && yoverlap && widthR > kWidthR && heightR > kHeightR) { // Found one, set nextblob type and recompute blob_box. found = true; nextblob->set_special_text_type(BSTT_SKIP); blob_box += nextblob_box; } } if (found) { blob->set_special_text_type(BSTT_SKIP); } } } int EquationDetect::FindEquationParts( ColPartitionGrid* part_grid, ColPartitionSet** best_columns) { if (!equ_tesseract_ || !lang_tesseract_) { tprintf("Warning: equ_tesseract_/lang_tesseract_ is NULL!\n"); return -1; } if (!part_grid || !best_columns) { tprintf("part_grid/best_columns is NULL!!\n"); return -1; } cp_seeds_.clear(); part_grid_ = part_grid; best_columns_ = best_columns; resolution_ = lang_tesseract_->source_resolution(); STRING outfile; page_count_++; if (equationdetect_save_bi_image) { GetOutputTiffName("_bi", &outfile); pixWrite(outfile.string(), lang_tesseract_->pix_binary(), IFF_TIFF_G4); } // Pass 0: Compute special text type for blobs. IdentifySpecialText(); // Pass 1: Merge parts by overlap. MergePartsByLocation(); // Pass 2: compute the math blob density and find the seed partition. IdentifySeedParts(); // We still need separate seed into block seed and inline seed partition. IdentifyInlineParts(); if (equationdetect_save_seed_image) { GetOutputTiffName("_seed", &outfile); PaintColParts(outfile); } // Pass 3: expand block equation seeds. while (!cp_seeds_.empty()) { GenericVector seeds_expanded; for (int i = 0; i < cp_seeds_.size(); ++i) { if (ExpandSeed(cp_seeds_[i])) { // If this seed is expanded, then we add it into seeds_expanded. Note // this seed has been removed from part_grid_ if it is expanded. seeds_expanded.push_back(cp_seeds_[i]); } } // Add seeds_expanded back into part_grid_ and reset cp_seeds_. for (int i = 0; i < seeds_expanded.size(); ++i) { InsertPartAfterAbsorb(seeds_expanded[i]); } cp_seeds_ = seeds_expanded; } // Pass 4: find math block satellite text partitions and merge them. ProcessMathBlockSatelliteParts(); if (equationdetect_save_merged_image) { // For debug. GetOutputTiffName("_merged", &outfile); PaintColParts(outfile); } return 0; } void EquationDetect::MergePartsByLocation() { while (true) { ColPartition* part = NULL; // partitions that have been updated. GenericVector parts_updated; ColPartitionGridSearch gsearch(part_grid_); gsearch.StartFullSearch(); while ((part = gsearch.NextFullSearch()) != NULL) { if (!IsTextOrEquationType(part->type())) { continue; } GenericVector parts_to_merge; SearchByOverlap(part, &parts_to_merge); if (parts_to_merge.empty()) { continue; } // Merge parts_to_merge with part, and remove them from part_grid_. part_grid_->RemoveBBox(part); for (int i = 0; i < parts_to_merge.size(); ++i) { ASSERT_HOST(parts_to_merge[i] != NULL && parts_to_merge[i] != part); part->Absorb(parts_to_merge[i], NULL); } gsearch.RepositionIterator(); parts_updated.push_back(part); } if (parts_updated.empty()) { // Exit the loop break; } // Re-insert parts_updated into part_grid_. for (int i = 0; i < parts_updated.size(); ++i) { InsertPartAfterAbsorb(parts_updated[i]); } } } void EquationDetect::SearchByOverlap( ColPartition* seed, GenericVector* parts_overlap) { ASSERT_HOST(seed != NULL && parts_overlap != NULL); if (!IsTextOrEquationType(seed->type())) { return; } ColPartitionGridSearch search(part_grid_); const TBOX& seed_box(seed->bounding_box()); const int kRadNeighborCells = 30; search.StartRadSearch((seed_box.left() + seed_box.right()) / 2, (seed_box.top() + seed_box.bottom()) / 2, kRadNeighborCells); search.SetUniqueMode(true); // Search iteratively. ColPartition *part; GenericVector parts; const float kLargeOverlapTh = 0.95; const float kEquXOverlap = 0.4, kEquYOverlap = 0.5; while ((part = search.NextRadSearch()) != NULL) { if (part == seed || !IsTextOrEquationType(part->type())) { continue; } const TBOX& part_box(part->bounding_box()); bool merge = false; float x_overlap_fraction = part_box.x_overlap_fraction(seed_box), y_overlap_fraction = part_box.y_overlap_fraction(seed_box); // If part is large overlapped with seed, then set merge to true. if (x_overlap_fraction >= kLargeOverlapTh && y_overlap_fraction >= kLargeOverlapTh) { merge = true; } else if (seed->type() == PT_EQUATION && IsTextOrEquationType(part->type())) { if ((x_overlap_fraction > kEquXOverlap && y_overlap_fraction > 0.0) || (x_overlap_fraction > 0.0 && y_overlap_fraction > kEquYOverlap)) { merge = true; } } if (merge) { // Remove the part from search and put it into parts. search.RemoveBBox(); parts_overlap->push_back(part); } } } void EquationDetect::InsertPartAfterAbsorb(ColPartition* part) { ASSERT_HOST(part); // Before insert part back into part_grid_, we will need re-compute some // of its attributes such as first_column_, last_column_. However, we still // want to preserve its type. BlobTextFlowType flow_type = part->flow(); PolyBlockType part_type = part->type(); BlobRegionType blob_type = part->blob_type(); // Call SetPartitionType to re-compute the attributes of part. const TBOX& part_box(part->bounding_box()); int grid_x, grid_y; part_grid_->GridCoords( part_box.left(), part_box.bottom(), &grid_x, &grid_y); part->SetPartitionType(resolution_, best_columns_[grid_y]); // Reset the types back. part->set_type(part_type); part->set_blob_type(blob_type); part->set_flow(flow_type); part->SetBlobTypes(); // Insert into part_grid_. part_grid_->InsertBBox(true, true, part); } void EquationDetect::IdentifySeedParts() { ColPartitionGridSearch gsearch(part_grid_); ColPartition *part = NULL; gsearch.StartFullSearch(); GenericVector seeds1, seeds2; // The left coordinates of indented text partitions. GenericVector indented_texts_left; // The foreground density of text partitions. GenericVector texts_foreground_density; while ((part = gsearch.NextFullSearch()) != NULL) { if (!IsTextOrEquationType(part->type())) { continue; } part->ComputeSpecialBlobsDensity(); bool blobs_check = CheckSeedBlobsCount(part); const int kTextBlobsTh = 20; if (CheckSeedDensity(kMathDigitDensityTh1, kMathDigitDensityTh2, part) && blobs_check) { // Passed high density threshold test, save into seeds1. seeds1.push_back(part); } else { IndentType indent = IsIndented(part); if (IsLeftIndented(indent) && blobs_check && CheckSeedDensity(kMathDigitDensityTh2, kMathDigitDensityTh2, part)) { // Passed low density threshold test and is indented, save into seeds2. seeds2.push_back(part); } else if (!IsRightIndented(indent) && part->boxes_count() > kTextBlobsTh) { // This is likely to be a text part, save the features. const TBOX&box = part->bounding_box(); if (IsLeftIndented(indent)) { indented_texts_left.push_back(box.left()); } texts_foreground_density.push_back(ComputeForegroundDensity(box)); } } } // Sort the features collected from text regions. indented_texts_left.sort(); texts_foreground_density.sort(); float foreground_density_th = 0.15; // Default value. if (!texts_foreground_density.empty()) { // Use the median of the texts_foreground_density. foreground_density_th = 0.8 * texts_foreground_density[ texts_foreground_density.size() / 2]; } for (int i = 0; i < seeds1.size(); ++i) { const TBOX& box = seeds1[i]->bounding_box(); if (CheckSeedFgDensity(foreground_density_th, seeds1[i]) && !(IsLeftIndented(IsIndented(seeds1[i])) && CountAlignment(indented_texts_left, box.left()) >= kLeftIndentAlignmentCountTh)) { // Mark as PT_EQUATION type. seeds1[i]->set_type(PT_EQUATION); cp_seeds_.push_back(seeds1[i]); } else { // Mark as PT_INLINE_EQUATION type. seeds1[i]->set_type(PT_INLINE_EQUATION); } } for (int i = 0; i < seeds2.size(); ++i) { if (CheckForSeed2(indented_texts_left, foreground_density_th, seeds2[i])) { seeds2[i]->set_type(PT_EQUATION); cp_seeds_.push_back(seeds2[i]); } } } float EquationDetect::ComputeForegroundDensity(const TBOX& tbox) { #if LIBLEPT_MINOR_VERSION < 69 && LIBLEPT_MAJOR_VERSION <= 1 // This will disable the detector because no seed will be identified. return 1.0f; #else Pix *pix_bi = lang_tesseract_->pix_binary(); int pix_height = pixGetHeight(pix_bi); Box* box = boxCreate(tbox.left(), pix_height - tbox.top(), tbox.width(), tbox.height()); Pix *pix_sub = pixClipRectangle(pix_bi, box, NULL); l_float32 fract; pixForegroundFraction(pix_sub, &fract); pixDestroy(&pix_sub); boxDestroy(&box); return fract; #endif } bool EquationDetect::CheckSeedFgDensity(const float density_th, ColPartition* part) { ASSERT_HOST(part); // Split part horizontall, and check for each sub part. GenericVector sub_boxes; SplitCPHorLite(part, &sub_boxes); float parts_passed = 0.0; for (int i = 0; i < sub_boxes.size(); ++i) { float density = ComputeForegroundDensity(sub_boxes[i]); if (density < density_th) { parts_passed++; } } // If most sub parts passed, then we return true. const float kSeedPartRatioTh = 0.3; bool retval = (parts_passed / sub_boxes.size() >= kSeedPartRatioTh); return retval; } void EquationDetect::SplitCPHor(ColPartition* part, GenericVector* parts_splitted) { ASSERT_HOST(part && parts_splitted); if (part->median_width() == 0 || part->boxes_count() == 0) { return; } // Make a copy of part, and reset parts_splitted. ColPartition* right_part = part->CopyButDontOwnBlobs(); parts_splitted->delete_data_pointers(); parts_splitted->clear(); const double kThreshold = part->median_width() * 3.0; bool found_split = true; while (found_split) { found_split = false; BLOBNBOX_C_IT box_it(right_part->boxes()); // Blobs are sorted left side first. If blobs overlap, // the previous blob may have a "more right" right side. // Account for this by always keeping the largest "right" // so far. int previous_right = MIN_INT32; // Look for the next split in the partition. for (box_it.mark_cycle_pt(); !box_it.cycled_list(); box_it.forward()) { const TBOX& box = box_it.data()->bounding_box(); if (previous_right != MIN_INT32 && box.left() - previous_right > kThreshold) { // We have a split position. Split the partition in two pieces. // Insert the left piece in the grid and keep processing the right. int mid_x = (box.left() + previous_right) / 2; ColPartition* left_part = right_part; right_part = left_part->SplitAt(mid_x); parts_splitted->push_back(left_part); left_part->ComputeSpecialBlobsDensity(); found_split = true; break; } // The right side of the previous blobs. previous_right = MAX(previous_right, box.right()); } } // Add the last piece. right_part->ComputeSpecialBlobsDensity(); parts_splitted->push_back(right_part); } void EquationDetect::SplitCPHorLite(ColPartition* part, GenericVector* splitted_boxes) { ASSERT_HOST(part && splitted_boxes); splitted_boxes->clear(); if (part->median_width() == 0) { return; } const double kThreshold = part->median_width() * 3.0; // Blobs are sorted left side first. If blobs overlap, // the previous blob may have a "more right" right side. // Account for this by always keeping the largest "right" // so far. TBOX union_box; int previous_right = MIN_INT32; BLOBNBOX_C_IT box_it(part->boxes()); for (box_it.mark_cycle_pt(); !box_it.cycled_list(); box_it.forward()) { const TBOX& box = box_it.data()->bounding_box(); if (previous_right != MIN_INT32 && box.left() - previous_right > kThreshold) { // We have a split position. splitted_boxes->push_back(union_box); previous_right = MIN_INT32; } if (previous_right == MIN_INT32) { union_box = box; } else { union_box += box; } // The right side of the previous blobs. previous_right = MAX(previous_right, box.right()); } // Add the last piece. if (previous_right != MIN_INT32) { splitted_boxes->push_back(union_box); } } bool EquationDetect::CheckForSeed2( const GenericVector& indented_texts_left, const float foreground_density_th, ColPartition* part) { ASSERT_HOST(part); const TBOX& box = part->bounding_box(); // Check if it is aligned with any indented_texts_left. if (!indented_texts_left.empty() && CountAlignment(indented_texts_left, box.left()) >= kLeftIndentAlignmentCountTh) { return false; } // Check the foreground density. if (ComputeForegroundDensity(box) > foreground_density_th) { return false; } return true; } int EquationDetect::CountAlignment( const GenericVector& sorted_vec, const int val) const { if (sorted_vec.empty()) { return 0; } const int kDistTh = static_cast(roundf(0.03 * resolution_)); int pos = sorted_vec.binary_search(val), count = 0; // Search left side. int index = pos; while (index >= 0 && abs(val - sorted_vec[index--]) < kDistTh) { count++; } // Search right side. index = pos + 1; while (index < sorted_vec.size() && sorted_vec[index++] - val < kDistTh) { count++; } return count; } void EquationDetect::IdentifyInlineParts() { ComputeCPsSuperBBox(); IdentifyInlinePartsHorizontal(); int textparts_linespacing = EstimateTextPartLineSpacing(); IdentifyInlinePartsVertical(true, textparts_linespacing); IdentifyInlinePartsVertical(false, textparts_linespacing); } void EquationDetect::ComputeCPsSuperBBox() { ColPartitionGridSearch gsearch(part_grid_); ColPartition *part = NULL; gsearch.StartFullSearch(); if (cps_super_bbox_) { delete cps_super_bbox_; } cps_super_bbox_ = new TBOX(); while ((part = gsearch.NextFullSearch()) != NULL) { (*cps_super_bbox_) += part->bounding_box(); } } void EquationDetect::IdentifyInlinePartsHorizontal() { ASSERT_HOST(cps_super_bbox_); GenericVector new_seeds; const int kMarginDiffTh = IntCastRounded( 0.5 * lang_tesseract_->source_resolution()); const int kGapTh = static_cast(roundf( 1.0 * lang_tesseract_->source_resolution())); ColPartitionGridSearch search(part_grid_); search.SetUniqueMode(true); // The center x coordinate of the cp_super_bbox_. int cps_cx = cps_super_bbox_->left() + cps_super_bbox_->width() / 2; for (int i = 0; i < cp_seeds_.size(); ++i) { ColPartition* part = cp_seeds_[i]; const TBOX& part_box(part->bounding_box()); int left_margin = part_box.left() - cps_super_bbox_->left(), right_margin = cps_super_bbox_->right() - part_box.right(); bool right_to_left; if (left_margin + kMarginDiffTh < right_margin && left_margin < kMarginDiffTh) { // part is left aligned, so we search if it has any right neighbor. search.StartSideSearch( part_box.right(), part_box.top(), part_box.bottom()); right_to_left = false; } else if (left_margin > cps_cx) { // part locates on the right half on image, so search if it has any left // neighbor. search.StartSideSearch( part_box.left(), part_box.top(), part_box.bottom()); right_to_left = true; } else { // part is not an inline equation. new_seeds.push_back(part); continue; } ColPartition* neighbor = NULL; bool side_neighbor_found = false; while ((neighbor = search.NextSideSearch(right_to_left)) != NULL) { const TBOX& neighbor_box(neighbor->bounding_box()); if (!IsTextOrEquationType(neighbor->type()) || part_box.x_gap(neighbor_box) > kGapTh || !part_box.major_y_overlap(neighbor_box) || part_box.major_x_overlap(neighbor_box)) { continue; } // We have found one. Set the side_neighbor_found flag. side_neighbor_found = true; break; } if (!side_neighbor_found) { // Mark part as PT_INLINE_EQUATION. part->set_type(PT_INLINE_EQUATION); } else { // Check the geometric feature of neighbor. const TBOX& neighbor_box(neighbor->bounding_box()); if (neighbor_box.width() > part_box.width() && neighbor->type() != PT_EQUATION) { // Mark as PT_INLINE_EQUATION. part->set_type(PT_INLINE_EQUATION); } else { // part is not an inline equation type. new_seeds.push_back(part); } } } // Reset the cp_seeds_ using the new_seeds. cp_seeds_ = new_seeds; } int EquationDetect::EstimateTextPartLineSpacing() { ColPartitionGridSearch gsearch(part_grid_); // Get the y gap between text partitions; ColPartition *current = NULL, *prev = NULL; gsearch.StartFullSearch(); GenericVector ygaps; while ((current = gsearch.NextFullSearch()) != NULL) { if (!PTIsTextType(current->type())) { continue; } if (prev != NULL) { const TBOX ¤t_box = current->bounding_box(); const TBOX &prev_box = prev->bounding_box(); // prev and current should be x major overlap and non y overlap. if (current_box.major_x_overlap(prev_box) && !current_box.y_overlap(prev_box)) { int gap = current_box.y_gap(prev_box); if (gap < MIN(current_box.height(), prev_box.height())) { // The gap should be smaller than the height of the bounding boxes. ygaps.push_back(gap); } } } prev = current; } if (ygaps.size() < 8) { // We do not have enough data. return -1; } // Compute the line spacing from ygaps: use the mean of the first half. ygaps.sort(); int spacing = 0, count; for (count = 0; count < ygaps.size() / 2; count++) { spacing += ygaps[count]; } return spacing / count; } void EquationDetect::IdentifyInlinePartsVertical( const bool top_to_bottom, const int textparts_linespacing) { if (cp_seeds_.empty()) { return; } // Sort cp_seeds_. if (top_to_bottom) { // From top to bottom. cp_seeds_.sort(&SortCPByTopReverse); } else { // From bottom to top. cp_seeds_.sort(&SortCPByBottom); } GenericVector new_seeds; for (int i = 0; i < cp_seeds_.size(); ++i) { ColPartition* part = cp_seeds_[i]; // If we sort cp_seeds_ from top to bottom, then for each cp_seeds_, we look // for its top neighbors, so that if two/more inline regions are connected // to each other, then we will identify the top one, and then use it to // identify the bottom one. if (IsInline(!top_to_bottom, textparts_linespacing, part)) { part->set_type(PT_INLINE_EQUATION); } else { new_seeds.push_back(part); } } cp_seeds_ = new_seeds; } bool EquationDetect::IsInline(const bool search_bottom, const int textparts_linespacing, ColPartition* part) { ASSERT_HOST(part != NULL); // Look for its nearest vertical neighbor that hardly overlaps in y but // largely overlaps in x. ColPartitionGridSearch search(part_grid_); ColPartition *neighbor = NULL; const TBOX& part_box(part->bounding_box()); const float kYGapRatioTh = 1.0; if (search_bottom) { search.StartVerticalSearch(part_box.left(), part_box.right(), part_box.bottom()); } else { search.StartVerticalSearch(part_box.left(), part_box.right(), part_box.top()); } search.SetUniqueMode(true); while ((neighbor = search.NextVerticalSearch(search_bottom)) != NULL) { const TBOX& neighbor_box(neighbor->bounding_box()); if (part_box.y_gap(neighbor_box) > kYGapRatioTh * MIN(part_box.height(), neighbor_box.height())) { // Finished searching. break; } if (!PTIsTextType(neighbor->type())) { continue; } // Check if neighbor and part is inline similar. const float kHeightRatioTh = 0.5; const int kYGapTh = textparts_linespacing > 0 ? textparts_linespacing + static_cast(roundf(0.02 * resolution_)): static_cast(roundf(0.05 * resolution_)); // Default value. if (part_box.x_overlap(neighbor_box) && // Location feature. part_box.y_gap(neighbor_box) <= kYGapTh && // Line spacing. // Geo feature. static_cast(MIN(part_box.height(), neighbor_box.height())) / MAX(part_box.height(), neighbor_box.height()) > kHeightRatioTh) { return true; } } return false; } bool EquationDetect::CheckSeedBlobsCount(ColPartition* part) { if (!part) { return false; } const int kSeedMathBlobsCount = 2; const int kSeedMathDigitBlobsCount = 5; int blobs = part->boxes_count(), math_blobs = part->SpecialBlobsCount(BSTT_MATH), digit_blobs = part->SpecialBlobsCount(BSTT_DIGIT); if (blobs < kSeedBlobsCountTh || math_blobs <= kSeedMathBlobsCount || math_blobs + digit_blobs <= kSeedMathDigitBlobsCount) { return false; } return true; } bool EquationDetect::CheckSeedDensity( const float math_density_high, const float math_density_low, const ColPartition* part) const { ASSERT_HOST(part); float math_digit_density = part->SpecialBlobsDensity(BSTT_MATH) + part->SpecialBlobsDensity(BSTT_DIGIT); float italic_density = part->SpecialBlobsDensity(BSTT_ITALIC); if (math_digit_density > math_density_high) { return true; } if (math_digit_density + italic_density > kMathItalicDensityTh && math_digit_density > math_density_low) { return true; } return false; } EquationDetect::IndentType EquationDetect::IsIndented(ColPartition* part) { ASSERT_HOST(part); ColPartitionGridSearch search(part_grid_); ColPartition *neighbor = NULL; const TBOX& part_box(part->bounding_box()); const int kXGapTh = static_cast(roundf(0.5 * resolution_)); const int kRadiusTh = static_cast(roundf(3.0 * resolution_)); const int kYGapTh = static_cast(roundf(0.5 * resolution_)); // Here we use a simple approximation algorithm: from the center of part, We // perform the radius search, and check if we can find a neighboring parition // that locates on the top/bottom left of part. search.StartRadSearch((part_box.left() + part_box.right()) / 2, (part_box.top() + part_box.bottom()) / 2, kRadiusTh); search.SetUniqueMode(true); bool left_indented = false, right_indented = false; while ((neighbor = search.NextRadSearch()) != NULL && (!left_indented || !right_indented)) { if (neighbor == part) { continue; } const TBOX& neighbor_box(neighbor->bounding_box()); if (part_box.major_y_overlap(neighbor_box) && part_box.x_gap(neighbor_box) < kXGapTh) { // When this happens, it is likely part is a fragment of an // over-segmented colpartition. So we return false. return NO_INDENT; } if (!IsTextOrEquationType(neighbor->type())) { continue; } // The neighbor should be above/below part, and overlap in x direction. if (!part_box.x_overlap(neighbor_box) || part_box.y_overlap(neighbor_box)) { continue; } if (part_box.y_gap(neighbor_box) < kYGapTh) { int left_gap = part_box.left() - neighbor_box.left(); int right_gap = neighbor_box.right() - part_box.right(); if (left_gap > kXGapTh) { left_indented = true; } if (right_gap > kXGapTh) { right_indented = true; } } } if (left_indented && right_indented) { return BOTH_INDENT; } if (left_indented) { return LEFT_INDENT; } if (right_indented) { return RIGHT_INDENT; } return NO_INDENT; } bool EquationDetect::ExpandSeed(ColPartition* seed) { if (seed == NULL || // This seed has been absorbed by other seeds. seed->IsVerticalType()) { // We skip vertical type right now. return false; } // Expand in four directions. GenericVector parts_to_merge; ExpandSeedHorizontal(true, seed, &parts_to_merge); ExpandSeedHorizontal(false, seed, &parts_to_merge); ExpandSeedVertical(true, seed, &parts_to_merge); ExpandSeedVertical(false, seed, &parts_to_merge); SearchByOverlap(seed, &parts_to_merge); if (parts_to_merge.empty()) { // We don't find any partition to merge. return false; } // Merge all partitions in parts_to_merge with seed. We first remove seed // from part_grid_ as its bounding box is going to expand. Then we add it // back after it aborbs all parts_to_merge parititions. part_grid_->RemoveBBox(seed); for (int i = 0; i < parts_to_merge.size(); ++i) { ColPartition* part = parts_to_merge[i]; if (part->type() == PT_EQUATION) { // If part is in cp_seeds_, then we mark it as NULL so that we won't // process it again. for (int j = 0; j < cp_seeds_.size(); ++j) { if (part == cp_seeds_[j]) { cp_seeds_[j] = NULL; break; } } } // part has already been removed from part_grid_ in function // ExpandSeedHorizontal/ExpandSeedVertical. seed->Absorb(part, NULL); } return true; } void EquationDetect::ExpandSeedHorizontal( const bool search_left, ColPartition* seed, GenericVector* parts_to_merge) { ASSERT_HOST(seed != NULL && parts_to_merge != NULL); const float kYOverlapTh = 0.6; const int kXGapTh = static_cast(roundf(0.2 * resolution_)); ColPartitionGridSearch search(part_grid_); const TBOX& seed_box(seed->bounding_box()); int x = search_left ? seed_box.left() : seed_box.right(); search.StartSideSearch(x, seed_box.bottom(), seed_box.top()); search.SetUniqueMode(true); // Search iteratively. ColPartition *part = NULL; while ((part = search.NextSideSearch(search_left)) != NULL) { if (part == seed) { continue; } const TBOX& part_box(part->bounding_box()); if (part_box.x_gap(seed_box) > kXGapTh) { // Out of scope. break; } // Check part location. if ((part_box.left() >= seed_box.left() && search_left) || (part_box.right() <= seed_box.right() && !search_left)) { continue; } if (part->type() != PT_EQUATION) { // Non-equation type. // Skip PT_LINLINE_EQUATION and non text type. if (part->type() == PT_INLINE_EQUATION || (!IsTextOrEquationType(part->type()) && part->blob_type() != BRT_HLINE)) { continue; } // For other types, it should be the near small neighbor of seed. if (!IsNearSmallNeighbor(seed_box, part_box) || !CheckSeedNeighborDensity(part)) { continue; } } else { // Equation type, check the y overlap. if (part_box.y_overlap_fraction(seed_box) < kYOverlapTh && seed_box.y_overlap_fraction(part_box) < kYOverlapTh) { continue; } } // Passed the check, delete it from search and add into parts_to_merge. search.RemoveBBox(); parts_to_merge->push_back(part); } } void EquationDetect::ExpandSeedVertical( const bool search_bottom, ColPartition* seed, GenericVector* parts_to_merge) { ASSERT_HOST(seed != NULL && parts_to_merge != NULL && cps_super_bbox_ != NULL); const float kXOverlapTh = 0.4; const int kYGapTh = static_cast(roundf(0.2 * resolution_)); ColPartitionGridSearch search(part_grid_); const TBOX& seed_box(seed->bounding_box()); int y = search_bottom ? seed_box.bottom() : seed_box.top(); search.StartVerticalSearch( cps_super_bbox_->left(), cps_super_bbox_->right(), y); search.SetUniqueMode(true); // Search iteratively. ColPartition *part = NULL; GenericVector parts; int skipped_min_top = INT_MAX, skipped_max_bottom = -1; while ((part = search.NextVerticalSearch(search_bottom)) != NULL) { if (part == seed) { continue; } const TBOX& part_box(part->bounding_box()); if (part_box.y_gap(seed_box) > kYGapTh) { // Out of scope. break; } // Check part location. if ((part_box.bottom() >= seed_box.bottom() && search_bottom) || (part_box.top() <= seed_box.top() && !search_bottom)) { continue; } bool skip_part = false; if (part->type() != PT_EQUATION) { // Non-equation type. // Skip PT_LINLINE_EQUATION and non text type. if (part->type() == PT_INLINE_EQUATION || (!IsTextOrEquationType(part->type()) && part->blob_type() != BRT_HLINE)) { skip_part = true; } else if (!IsNearSmallNeighbor(seed_box, part_box) || !CheckSeedNeighborDensity(part)) { // For other types, it should be the near small neighbor of seed. skip_part = true; } } else { // Equation type, check the x overlap. if (part_box.x_overlap_fraction(seed_box) < kXOverlapTh && seed_box.x_overlap_fraction(part_box) < kXOverlapTh) { skip_part = true; } } if (skip_part) { if (part->type() != PT_EQUATION) { if (skipped_min_top > part_box.top()) { skipped_min_top = part_box.top(); } if (skipped_max_bottom < part_box.bottom()) { skipped_max_bottom = part_box.bottom(); } } } else { parts.push_back(part); } } // For every part in parts, we need verify it is not above skipped_min_top // when search top, or not below skipped_max_bottom when search bottom. I.e., // we will skip a part if it looks like: // search bottom | search top // seed: ****************** | part: ********** // skipped: xxx | skipped: xxx // part: ********** | seed: *********** for (int i = 0; i < parts.size(); i++) { const TBOX& part_box(parts[i]->bounding_box()); if ((search_bottom && part_box.top() <= skipped_max_bottom) || (!search_bottom && part_box.bottom() >= skipped_min_top)) { continue; } // Add parts[i] into parts_to_merge, and delete it from part_grid_. parts_to_merge->push_back(parts[i]); part_grid_->RemoveBBox(parts[i]); } } bool EquationDetect::IsNearSmallNeighbor(const TBOX& seed_box, const TBOX& part_box) const { const int kXGapTh = static_cast(roundf(0.25 * resolution_)); const int kYGapTh = static_cast(roundf(0.05 * resolution_)); // Check geometric feature. if (part_box.height() > seed_box.height() || part_box.width() > seed_box.width()) { return false; } // Check overlap and distance. if ((!part_box.major_x_overlap(seed_box) || part_box.y_gap(seed_box) > kYGapTh) && (!part_box.major_y_overlap(seed_box) || part_box.x_gap(seed_box) > kXGapTh)) { return false; } return true; } bool EquationDetect::CheckSeedNeighborDensity(const ColPartition* part) const { ASSERT_HOST(part); if (part->boxes_count() < kSeedBlobsCountTh) { // Too few blobs, skip the check. return true; } // We check the math blobs density and the unclear blobs density. if (part->SpecialBlobsDensity(BSTT_MATH) + part->SpecialBlobsDensity(BSTT_DIGIT) > kMathDigitDensityTh1 || part->SpecialBlobsDensity(BSTT_UNCLEAR) > kUnclearDensityTh) { return true; } return false; } void EquationDetect::ProcessMathBlockSatelliteParts() { // Iterate over part_grid_, and find all parts that are text type but not // equation type. ColPartition *part = NULL; GenericVector text_parts; ColPartitionGridSearch gsearch(part_grid_); gsearch.StartFullSearch(); while ((part = gsearch.NextFullSearch()) != NULL) { if (part->type() == PT_FLOWING_TEXT || part->type() == PT_HEADING_TEXT) { text_parts.push_back(part); } } if (text_parts.empty()) { return; } // Compute the medium height of the text_parts. text_parts.sort(&SortCPByHeight); const TBOX& text_box = text_parts[text_parts.size() / 2]->bounding_box(); int med_height = text_box.height(); if (text_parts.size() % 2 == 0 && text_parts.size() > 1) { const TBOX& text_box = text_parts[text_parts.size() / 2 - 1]->bounding_box(); med_height = static_cast(roundf( 0.5 * (text_box.height() + med_height))); } // Iterate every text_parts and check if it is a math block satellite. for (int i = 0; i < text_parts.size(); ++i) { const TBOX& text_box(text_parts[i]->bounding_box()); if (text_box.height() > med_height) { continue; } GenericVector math_blocks; if (!IsMathBlockSatellite(text_parts[i], &math_blocks)) { continue; } // Found. merge text_parts[i] with math_blocks. part_grid_->RemoveBBox(text_parts[i]); text_parts[i]->set_type(PT_EQUATION); for (int j = 0; j < math_blocks.size(); ++j) { part_grid_->RemoveBBox(math_blocks[j]); text_parts[i]->Absorb(math_blocks[j], NULL); } InsertPartAfterAbsorb(text_parts[i]); } } bool EquationDetect::IsMathBlockSatellite( ColPartition* part, GenericVector* math_blocks) { ASSERT_HOST(part != NULL && math_blocks != NULL); math_blocks->clear(); const TBOX& part_box(part->bounding_box()); // Find the top/bottom nearest neighbor of part. ColPartition *neighbors[2]; int y_gaps[2] = {INT_MAX, INT_MAX}; // The horizontal boundary of the neighbors. int neighbors_left = INT_MAX, neighbors_right = 0; for (int i = 0; i < 2; ++i) { neighbors[i] = SearchNNVertical(i != 0, part); if (neighbors[i]) { const TBOX& neighbor_box = neighbors[i]->bounding_box(); y_gaps[i] = neighbor_box.y_gap(part_box); if (neighbor_box.left() < neighbors_left) { neighbors_left = neighbor_box.left(); } if (neighbor_box.right() > neighbors_right) { neighbors_right = neighbor_box.right(); } } } if (neighbors[0] == neighbors[1]) { // This happens when part is inside neighbor. neighbors[1] = NULL; y_gaps[1] = INT_MAX; } // Check if part is within [neighbors_left, neighbors_right]. if (part_box.left() < neighbors_left || part_box.right() > neighbors_right) { return false; } // Get the index of the near one in neighbors. int index = y_gaps[0] < y_gaps[1] ? 0 : 1; // Check the near one. if (IsNearMathNeighbor(y_gaps[index], neighbors[index])) { math_blocks->push_back(neighbors[index]); } else { // If the near one failed the check, then we skip checking the far one. return false; } // Check the far one. index = 1 - index; if (IsNearMathNeighbor(y_gaps[index], neighbors[index])) { math_blocks->push_back(neighbors[index]); } return true; } ColPartition* EquationDetect::SearchNNVertical( const bool search_bottom, const ColPartition* part) { ASSERT_HOST(part); ColPartition *nearest_neighbor = NULL, *neighbor = NULL; const int kYGapTh = static_cast(roundf(resolution_ * 0.5)); ColPartitionGridSearch search(part_grid_); search.SetUniqueMode(true); const TBOX& part_box(part->bounding_box()); int y = search_bottom ? part_box.bottom() : part_box.top(); search.StartVerticalSearch(part_box.left(), part_box.right(), y); int min_y_gap = INT_MAX; while ((neighbor = search.NextVerticalSearch(search_bottom)) != NULL) { if (neighbor == part || !IsTextOrEquationType(neighbor->type())) { continue; } const TBOX& neighbor_box(neighbor->bounding_box()); int y_gap = neighbor_box.y_gap(part_box); if (y_gap > kYGapTh) { // Out of scope. break; } if (!neighbor_box.major_x_overlap(part_box) || (search_bottom && neighbor_box.bottom() > part_box.bottom()) || (!search_bottom && neighbor_box.top() < part_box.top())) { continue; } if (y_gap < min_y_gap) { min_y_gap = y_gap; nearest_neighbor = neighbor; } } return nearest_neighbor; } bool EquationDetect::IsNearMathNeighbor( const int y_gap, const ColPartition *neighbor) const { if (!neighbor) { return false; } const int kYGapTh = static_cast(roundf(resolution_ * 0.1)); return neighbor->type() == PT_EQUATION && y_gap <= kYGapTh; } void EquationDetect::GetOutputTiffName(const char* name, STRING* image_name) const { ASSERT_HOST(image_name && name); char page[50]; snprintf(page, sizeof(page), "%04d", page_count_); *image_name = STRING(lang_tesseract_->imagebasename) + page + name + ".tif"; } void EquationDetect::PaintSpecialTexts(const STRING& outfile) const { Pix *pix = NULL, *pixBi = lang_tesseract_->pix_binary(); pix = pixConvertTo32(pixBi); ColPartitionGridSearch gsearch(part_grid_); ColPartition* part = NULL; gsearch.StartFullSearch(); while ((part = gsearch.NextFullSearch()) != NULL) { BLOBNBOX_C_IT blob_it(part->boxes()); for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { RenderSpecialText(pix, blob_it.data()); } } pixWrite(outfile.string(), pix, IFF_TIFF_LZW); pixDestroy(&pix); } void EquationDetect::PaintColParts(const STRING& outfile) const { Pix *pix = pixConvertTo32(lang_tesseract_->BestPix()); ColPartitionGridSearch gsearch(part_grid_); gsearch.StartFullSearch(); ColPartition* part = NULL; while ((part = gsearch.NextFullSearch()) != NULL) { const TBOX& tbox = part->bounding_box(); Box *box = boxCreate(tbox.left(), pixGetHeight(pix) - tbox.top(), tbox.width(), tbox.height()); if (part->type() == PT_EQUATION) { pixRenderBoxArb(pix, box, 5, 255, 0, 0); } else if (part->type() == PT_INLINE_EQUATION) { pixRenderBoxArb(pix, box, 5, 0, 255, 0); } else { pixRenderBoxArb(pix, box, 5, 0, 0, 255); } boxDestroy(&box); } pixWrite(outfile.string(), pix, IFF_TIFF_LZW); pixDestroy(&pix); } void EquationDetect::PrintSpecialBlobsDensity(const ColPartition* part) const { ASSERT_HOST(part); TBOX box(part->bounding_box()); int h = pixGetHeight(lang_tesseract_->BestPix()); tprintf("Printing special blobs density values for ColParition (t=%d,b=%d) ", h - box.top(), h - box.bottom()); box.print(); tprintf("blobs count = %d, density = ", part->boxes_count()); for (int i = 0; i < BSTT_COUNT; ++i) { BlobSpecialTextType type = static_cast(i); tprintf("%d:%f ", i, part->SpecialBlobsDensity(type)); } tprintf("\n"); } }; // namespace tesseract tesseract-3.04.01/ccmain/equationdetect.h000066400000000000000000000256561266071204500203160ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: equationdetect.h // Description: The equation detection class that inherits equationdetectbase. // Author: Zongyi (Joe) Liu (joeliu@google.com) // Created: Fri Aug 31 11:13:01 PST 2011 // // (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_CCMAIN_EQUATIONDETECT_H__ #define TESSERACT_CCMAIN_EQUATIONDETECT_H__ #include "blobbox.h" #include "equationdetectbase.h" #include "genericvector.h" #include "unichar.h" class BLOBNBOX; class BLOB_CHOICE; class BLOB_CHOICE_LIST; class TO_BLOCK_LIST; class TBOX; class UNICHARSET; namespace tesseract { class Tesseract; class ColPartition; class ColPartitionGrid; class ColPartitionSet; class EquationDetect : public EquationDetectBase { public: EquationDetect(const char* equ_datapath, const char* equ_language); ~EquationDetect(); enum IndentType { NO_INDENT, LEFT_INDENT, RIGHT_INDENT, BOTH_INDENT, INDENT_TYPE_COUNT }; // Reset the lang_tesseract_ pointer. This function should be called before we // do any detector work. void SetLangTesseract(Tesseract* lang_tesseract); // Iterate over the blobs inside to_block, and set the blobs that we want to // process to BSTT_NONE. (By default, they should be BSTT_SKIP). The function // returns 0 upon success. int LabelSpecialText(TO_BLOCK* to_block); // Find possible equation partitions from part_grid. Should be called // after the special_text_type of blobs are set. // It returns 0 upon success. int FindEquationParts(ColPartitionGrid* part_grid, ColPartitionSet** best_columns); // Reset the resolution of the processing image. TEST only function. void SetResolution(const int resolution); protected: // Identify the special text type for one blob, and update its field. When // height_th is set (> 0), we will label the blob as BSTT_NONE if its height // is less than height_th. void IdentifySpecialText(BLOBNBOX *blob, const int height_th); // Estimate the type for one unichar. BlobSpecialTextType EstimateTypeForUnichar( const UNICHARSET& unicharset, const UNICHAR_ID id) const; // Compute special text type for each blobs in part_grid_. void IdentifySpecialText(); // Identify blobs that we want to skip during special blob type // classification. void IdentifyBlobsToSkip(ColPartition* part); // The ColPartitions in part_grid_ maybe over-segmented, particularly in the // block equation regions. So we like to identify these partitions and merge // them before we do the searching. void MergePartsByLocation(); // Staring from the seed center, we do radius search. And for partitions that // have large overlaps with seed, we remove them from part_grid_ and add into // parts_overlap. Note: this function may update the part_grid_, so if the // caller is also running ColPartitionGridSearch, use the RepositionIterator // to continue. void SearchByOverlap(ColPartition* seed, GenericVector* parts_overlap); // Insert part back into part_grid_, after it absorbs some other parts. void InsertPartAfterAbsorb(ColPartition* part); // Identify the colparitions in part_grid_, label them as PT_EQUATION, and // save them into cp_seeds_. void IdentifySeedParts(); // Check the blobs count for a seed region candidate. bool CheckSeedBlobsCount(ColPartition* part); // Compute the foreground pixel density for a tbox area. float ComputeForegroundDensity(const TBOX& tbox); // Check if part from seed2 label: with low math density and left indented. We // are using two checks: // 1. If its left is aligned with any coordinates in indented_texts_left, // which we assume have been sorted. // 2. If its foreground density is over foreground_density_th. bool CheckForSeed2( const GenericVector& indented_texts_left, const float foreground_density_th, ColPartition* part); // Count the number of values in sorted_vec that is close to val, used to // check if a partition is aligned with text partitions. int CountAlignment( const GenericVector& sorted_vec, const int val) const; // Check for a seed candidate using the foreground pixel density. And we // return true if the density is below a certain threshold, because characters // in equation regions usually are apart with more white spaces. bool CheckSeedFgDensity(const float density_th, ColPartition* part); // A light version of SplitCPHor: instead of really doing the part split, we // simply compute the union bounding box of each splitted part. void SplitCPHorLite(ColPartition* part, GenericVector* splitted_boxes); // Split the part (horizontally), and save the splitted result into // parts_splitted. Note that it is caller's responsibility to release the // memory owns by parts_splitted. On the other hand, the part is unchanged // during this process and still owns the blobs, so do NOT call DeleteBoxes // when freeing the colpartitions in parts_splitted. void SplitCPHor(ColPartition* part, GenericVector* parts_splitted); // Check the density for a seed candidate (part) using its math density and // italic density, returns true if the check passed. bool CheckSeedDensity(const float math_density_high, const float math_density_low, const ColPartition* part) const; // Check if part is indented. IndentType IsIndented(ColPartition* part); // Identify inline partitions from cp_seeds_, and re-label them. void IdentifyInlineParts(); // Comute the super bounding box for all colpartitions inside part_grid_. void ComputeCPsSuperBBox(); // Identify inline partitions from cp_seeds_ using the horizontal search. void IdentifyInlinePartsHorizontal(); // Estimate the line spacing between two text partitions. Returns -1 if not // enough data. int EstimateTextPartLineSpacing(); // Identify inline partitions from cp_seeds_ using vertical search. void IdentifyInlinePartsVertical(const bool top_to_bottom, const int textPartsLineSpacing); // Check if part is an inline equation zone. This should be called after we // identified the seed regions. bool IsInline(const bool search_bottom, const int textPartsLineSpacing, ColPartition* part); // For a given seed partition, we search the part_grid_ and see if there is // any partition can be merged with it. It returns true if the seed has been // expanded. bool ExpandSeed(ColPartition* seed); // Starting from the seed position, we search the part_grid_ // horizontally/vertically, find all parititions that can be // merged with seed, remove them from part_grid_, and put them into // parts_to_merge. void ExpandSeedHorizontal(const bool search_left, ColPartition* seed, GenericVector* parts_to_merge); void ExpandSeedVertical(const bool search_bottom, ColPartition* seed, GenericVector* parts_to_merge); // Check if a part_box is the small neighbor of seed_box. bool IsNearSmallNeighbor(const TBOX& seed_box, const TBOX& part_box) const; // Perform the density check for part, which we assume is nearing a seed // partition. It returns true if the check passed. bool CheckSeedNeighborDensity(const ColPartition* part) const; // After identify the math blocks, we do one more scanning on all text // partitions, and check if any of them is the satellite of: // math blocks: here a p is the satellite of q if: // 1. q is the nearest vertical neighbor of p, and // 2. y_gap(p, q) is less than a threshold, and // 3. x_overlap(p, q) is over a threshold. // Note that p can be the satellites of two blocks: its top neighbor and // bottom neighbor. void ProcessMathBlockSatelliteParts(); // Check if part is the satellite of one/two math blocks. If it is, we return // true, and save the blocks into math_blocks. bool IsMathBlockSatellite( ColPartition* part, GenericVector* math_blocks); // Search the nearest neighbor of part in one vertical direction as defined in // search_bottom. It returns the neighbor found that major x overlap with it, // or NULL when not found. ColPartition* SearchNNVertical(const bool search_bottom, const ColPartition* part); // Check if the neighbor with vertical distance of y_gap is a near and math // block partition. bool IsNearMathNeighbor(const int y_gap, const ColPartition *neighbor) const; // Generate the tiff file name for output/debug file. void GetOutputTiffName(const char* name, STRING* image_name) const; // Debugger function that renders ColPartitions on the input image, where: // parts labeled as PT_EQUATION will be painted in red, PT_INLINE_EQUATION // will be painted in green, and other parts will be painted in blue. void PaintColParts(const STRING& outfile) const; // Debugger function that renders the blobs in part_grid_ over the input // image. void PaintSpecialTexts(const STRING& outfile) const; // Debugger function that print the math blobs density values for a // ColPartition object. void PrintSpecialBlobsDensity(const ColPartition* part) const; // The tesseract engine intialized from equation training data. Tesseract* equ_tesseract_; // The tesseract engine used for OCR. This pointer is passed in by the caller, // so do NOT destroy it in this class. Tesseract* lang_tesseract_; // The ColPartitionGrid that we are processing. This pointer is passed in from // the caller, so do NOT destroy it in the class. ColPartitionGrid* part_grid_; // A simple array of pointers to the best assigned column division at // each grid y coordinate. This pointer is passed in from the caller, so do // NOT destroy it in the class. ColPartitionSet** best_columns_; // The super bounding box of all cps in the part_grid_. TBOX* cps_super_bbox_; // The seed ColPartition for equation region. GenericVector cp_seeds_; // The resolution (dpi) of the processing image. int resolution_; // The number of pages we have processed. int page_count_; }; } // namespace tesseract #endif // TESSERACT_CCMAIN_EQUATIONDETECT_H_ tesseract-3.04.01/ccmain/fixspace.cpp000066400000000000000000000716361266071204500174340ustar00rootroot00000000000000/****************************************************************** * File: fixspace.cpp (Formerly fixspace.c) * Description: Implements a pass over the page res, exploring the alternative * spacing possibilities, trying to use context to improve the * word spacing * Author: Phil Cheatle * Created: Thu Oct 21 11:38:43 BST 1993 * * (C) Copyright 1993, 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. * **********************************************************************/ #include #include "reject.h" #include "statistc.h" #include "control.h" #include "fixspace.h" #include "genblob.h" #include "tessvars.h" #include "tessbox.h" #include "globals.h" #include "tesseractclass.h" #define PERFECT_WERDS 999 #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 * them as a sublist, process the sublist to find the optimal arrangement of * spaces then replace the sublist in the ROW_RES. * * @param monitor progress monitor * @param word_count count of words in doc * @param[out] page_res */ void Tesseract::fix_fuzzy_spaces(ETEXT_DESC *monitor, inT32 word_count, PAGE_RES *page_res) { BLOCK_RES_IT block_res_it; ROW_RES_IT row_res_it; WERD_RES_IT word_res_it_from; WERD_RES_IT word_res_it_to; WERD_RES *word_res; WERD_RES_LIST fuzzy_space_words; inT16 new_length; BOOL8 prevent_null_wd_fixsp; // DON'T process blobless wds inT32 word_index; // current word block_res_it.set_to_list(&page_res->block_res_list); word_index = 0; for (block_res_it.mark_cycle_pt(); !block_res_it.cycled_list(); block_res_it.forward()) { row_res_it.set_to_list(&block_res_it.data()->row_res_list); for (row_res_it.mark_cycle_pt(); !row_res_it.cycled_list(); row_res_it.forward()) { word_res_it_from.set_to_list(&row_res_it.data()->word_res_list); while (!word_res_it_from.at_last()) { word_res = word_res_it_from.data(); while (!word_res_it_from.at_last() && !(word_res->combination || word_res_it_from.data_relative(1)->word->flag(W_FUZZY_NON) || word_res_it_from.data_relative(1)->word->flag(W_FUZZY_SP))) { fix_sp_fp_word(word_res_it_from, row_res_it.data()->row, block_res_it.data()->block); word_res = word_res_it_from.forward(); word_index++; if (monitor != NULL) { monitor->ocr_alive = TRUE; monitor->progress = 90 + 5 * word_index / word_count; if (monitor->deadline_exceeded() || (monitor->cancel != NULL && (*monitor->cancel)(monitor->cancel_this, stats_.dict_words))) return; } } if (!word_res_it_from.at_last()) { word_res_it_to = word_res_it_from; prevent_null_wd_fixsp = word_res->word->cblob_list()->empty(); if (check_debug_pt(word_res, 60)) debug_fix_space_level.set_value(10); word_res_it_to.forward(); word_index++; if (monitor != NULL) { monitor->ocr_alive = TRUE; monitor->progress = 90 + 5 * word_index / word_count; if (monitor->deadline_exceeded() || (monitor->cancel != NULL && (*monitor->cancel)(monitor->cancel_this, stats_.dict_words))) return; } while (!word_res_it_to.at_last () && (word_res_it_to.data_relative(1)->word->flag(W_FUZZY_NON) || word_res_it_to.data_relative(1)->word->flag(W_FUZZY_SP))) { if (check_debug_pt(word_res, 60)) debug_fix_space_level.set_value(10); if (word_res->word->cblob_list()->empty()) prevent_null_wd_fixsp = TRUE; word_res = word_res_it_to.forward(); } if (check_debug_pt(word_res, 60)) debug_fix_space_level.set_value(10); if (word_res->word->cblob_list()->empty()) prevent_null_wd_fixsp = TRUE; if (prevent_null_wd_fixsp) { word_res_it_from = word_res_it_to; } else { fuzzy_space_words.assign_to_sublist(&word_res_it_from, &word_res_it_to); fix_fuzzy_space_list(fuzzy_space_words, row_res_it.data()->row, block_res_it.data()->block); new_length = fuzzy_space_words.length(); word_res_it_from.add_list_before(&fuzzy_space_words); for (; !word_res_it_from.at_last() && new_length > 0; new_length--) { word_res_it_from.forward(); } } if (test_pt) debug_fix_space_level.set_value(0); } fix_sp_fp_word(word_res_it_from, row_res_it.data()->row, block_res_it.data()->block); // Last word in row } } } } void Tesseract::fix_fuzzy_space_list(WERD_RES_LIST &best_perm, ROW *row, BLOCK* block) { inT16 best_score; WERD_RES_LIST current_perm; inT16 current_score; BOOL8 improved = FALSE; best_score = eval_word_spacing(best_perm); // default score dump_words(best_perm, best_score, 1, improved); if (best_score != PERFECT_WERDS) initialise_search(best_perm, current_perm); while ((best_score != PERFECT_WERDS) && !current_perm.empty()) { match_current_words(current_perm, row, block); current_score = eval_word_spacing(current_perm); dump_words(current_perm, current_score, 2, improved); if (current_score > best_score) { best_perm.clear(); best_perm.deep_copy(¤t_perm, &WERD_RES::deep_copy); best_score = current_score; improved = TRUE; } if (current_score < PERFECT_WERDS) transform_to_next_perm(current_perm); } dump_words(best_perm, best_score, 3, improved); } } // namespace tesseract void initialise_search(WERD_RES_LIST &src_list, WERD_RES_LIST &new_list) { WERD_RES_IT src_it(&src_list); WERD_RES_IT new_it(&new_list); WERD_RES *src_wd; WERD_RES *new_wd; for (src_it.mark_cycle_pt(); !src_it.cycled_list(); src_it.forward()) { src_wd = src_it.data(); if (!src_wd->combination) { 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); } } } namespace tesseract { void Tesseract::match_current_words(WERD_RES_LIST &words, ROW *row, BLOCK* block) { WERD_RES_IT word_it(&words); WERD_RES *word; // Since we are not using PAGE_RES to iterate over words, we need to update // prev_word_best_choice_ before calling classify_word_pass2(). prev_word_best_choice_ = NULL; for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) { word = word_it.data(); if ((!word->part_of_combo) && (word->box_word == NULL)) { WordData word_data(block, row, word); SetupWordPassN(2, &word_data); classify_word_and_language(2, NULL, &word_data); } prev_word_best_choice_ = word->best_choice; } } /** * @name eval_word_spacing() * The basic measure is the number of characters in contextually confirmed * words. (I.e the word is done) * If all words are contextually confirmed the evaluation is deemed perfect. * * Some fiddles are done to handle "1"s as these are VERY frequent causes of * fuzzy spaces. The problem with the basic measure is that "561 63" would score * the same as "56163", though given our knowledge that the space is fuzzy, and * that there is a "1" next to the fuzzy space, we need to ensure that "56163" * is preferred. * * The solution is to NOT COUNT the score of any word which has a digit at one * end and a "1Il" as the character the other side of the space. * * Conversly, any character next to a "1" within a word is counted as a positive * score. Thus "561 63" would score 4 (3 chars in a numeric word plus 1 side of * the "1" joined). "56163" would score 7 - all chars in a numeric word + 2 * sides of a "1" joined. * * The joined 1 rule is applied to any word REGARDLESS of contextual * confirmation. Thus "PS7a71 3/7a" scores 1 (neither word is contexutally * confirmed. The only score is from the joined 1. "PS7a713/7a" scores 2. * */ inT16 Tesseract::eval_word_spacing(WERD_RES_LIST &word_res_list) { WERD_RES_IT word_res_it(&word_res_list); inT16 total_score = 0; inT16 word_count = 0; inT16 done_word_count = 0; inT16 word_len; inT16 i; inT16 offset; WERD_RES *word; // current word inT16 prev_word_score = 0; BOOL8 prev_word_done = FALSE; BOOL8 prev_char_1 = FALSE; // prev ch a "1/I/l"? BOOL8 prev_char_digit = FALSE; // prev ch 2..9 or 0 BOOL8 current_char_1 = FALSE; BOOL8 current_word_ok_so_far; STRING punct_chars = "!\"`',.:;"; BOOL8 prev_char_punct = FALSE; BOOL8 current_char_punct = FALSE; BOOL8 word_done = FALSE; do { word = word_res_it.data(); word_done = fixspace_thinks_word_done(word); word_count++; if (word->tess_failed) { total_score += prev_word_score; if (prev_word_done) done_word_count++; prev_word_score = 0; prev_char_1 = FALSE; prev_char_digit = FALSE; prev_word_done = FALSE; } else { /* Can we add the prev word score and potentially count this word? Yes IF it didn't end in a 1 when the first char of this word is a digit AND it didn't end in a digit when the first char of this word is a 1 */ word_len = word->reject_map.length(); current_word_ok_so_far = FALSE; if (!((prev_char_1 && digit_or_numeric_punct(word, 0)) || (prev_char_digit && ( (word_done && word->best_choice->unichar_lengths().string()[0] == 1 && word->best_choice->unichar_string()[0] == '1') || (!word_done && STRING(conflict_set_I_l_1).contains( word->best_choice->unichar_string()[0])))))) { total_score += prev_word_score; if (prev_word_done) done_word_count++; current_word_ok_so_far = word_done; } if (current_word_ok_so_far) { prev_word_done = TRUE; prev_word_score = word_len; } else { prev_word_done = FALSE; prev_word_score = 0; } /* Add 1 to total score for every joined 1 regardless of context and rejtn */ for (i = 0, prev_char_1 = FALSE; i < word_len; i++) { current_char_1 = word->best_choice->unichar_string()[i] == '1'; if (prev_char_1 || (current_char_1 && (i > 0))) total_score++; prev_char_1 = current_char_1; } /* Add 1 to total score for every joined punctuation regardless of context and rejtn */ if (tessedit_prefer_joined_punct) { for (i = 0, offset = 0, prev_char_punct = FALSE; i < word_len; offset += word->best_choice->unichar_lengths()[i++]) { current_char_punct = punct_chars.contains(word->best_choice->unichar_string()[offset]); if (prev_char_punct || (current_char_punct && i > 0)) total_score++; prev_char_punct = current_char_punct; } } prev_char_digit = digit_or_numeric_punct(word, word_len - 1); for (i = 0, offset = 0; i < word_len - 1; offset += word->best_choice->unichar_lengths()[i++]); prev_char_1 = ((word_done && (word->best_choice->unichar_string()[offset] == '1')) || (!word_done && STRING(conflict_set_I_l_1).contains( word->best_choice->unichar_string()[offset]))); } /* Find next word */ do { word_res_it.forward(); } while (word_res_it.data()->part_of_combo); } while (!word_res_it.at_first()); total_score += prev_word_score; if (prev_word_done) done_word_count++; if (done_word_count == word_count) return PERFECT_WERDS; else return total_score; } BOOL8 Tesseract::digit_or_numeric_punct(WERD_RES *word, int char_position) { int i; int offset; for (i = 0, offset = 0; i < char_position; offset += word->best_choice->unichar_lengths()[i++]); return ( word->uch_set->get_isdigit( word->best_choice->unichar_string().string() + offset, word->best_choice->unichar_lengths()[i]) || (word->best_choice->permuter() == NUMBER_PERM && STRING(numeric_punctuation).contains( word->best_choice->unichar_string().string()[offset]))); } } // namespace tesseract /** * @name transform_to_next_perm() * Examines the current word list to find the smallest word gap size. Then walks * the word list closing any gaps of this size by either inserted new * combination words, or extending existing ones. * * The routine COULD be limited to stop it building words longer than N blobs. * * If there are no more gaps then it DELETES the entire list and returns the * empty list to cause termination. */ void transform_to_next_perm(WERD_RES_LIST &words) { WERD_RES_IT word_it(&words); WERD_RES_IT prev_word_it(&words); WERD_RES *word; WERD_RES *prev_word; WERD_RES *combo; WERD *copy_word; inT16 prev_right = -MAX_INT16; TBOX box; inT16 gap; inT16 min_gap = MAX_INT16; for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) { word = word_it.data(); if (!word->part_of_combo) { box = word->word->bounding_box(); if (prev_right > -MAX_INT16) { gap = box.left() - prev_right; if (gap < min_gap) min_gap = gap; } prev_right = box.right(); } } if (min_gap < MAX_INT16) { prev_right = -MAX_INT16; // back to start word_it.set_to_list(&words); // Note: we can't use cycle_pt due to inserted combos at start of list. for (; (prev_right == -MAX_INT16) || !word_it.at_first(); word_it.forward()) { word = word_it.data(); if (!word->part_of_combo) { box = word->word->bounding_box(); if (prev_right > -MAX_INT16) { gap = box.left() - prev_right; if (gap <= min_gap) { prev_word = prev_word_it.data(); if (prev_word->combination) { combo = prev_word; } else { /* Make a new combination and insert before * the first word being joined. */ copy_word = new WERD; *copy_word = *(prev_word->word); // deep copy combo = new WERD_RES(copy_word); combo->combination = TRUE; combo->x_height = prev_word->x_height; prev_word->part_of_combo = TRUE; prev_word_it.add_before_then_move(combo); } combo->word->set_flag(W_EOL, word->word->flag(W_EOL)); if (word->combination) { combo->word->join_on(word->word); // Move blobs to combo // old combo no longer needed delete word_it.extract(); } else { // Copy current wd to combo combo->copy_on(word); word->part_of_combo = TRUE; } combo->done = FALSE; combo->ClearResults(); } else { prev_word_it = word_it; // catch up } } prev_right = box.right(); } } } else { words.clear(); // signal termination } } namespace tesseract { void Tesseract::dump_words(WERD_RES_LIST &perm, inT16 score, inT16 mode, BOOL8 improved) { WERD_RES_IT word_res_it(&perm); if (debug_fix_space_level > 0) { if (mode == 1) { stats_.dump_words_str = ""; for (word_res_it.mark_cycle_pt(); !word_res_it.cycled_list(); word_res_it.forward()) { if (!word_res_it.data()->part_of_combo) { stats_.dump_words_str += word_res_it.data()->best_choice->unichar_string(); stats_.dump_words_str += ' '; } } } if (debug_fix_space_level > 1) { switch (mode) { case 1: tprintf("EXTRACTED (%d): \"", score); break; case 2: tprintf("TESTED (%d): \"", score); break; case 3: tprintf("RETURNED (%d): \"", score); break; } for (word_res_it.mark_cycle_pt(); !word_res_it.cycled_list(); word_res_it.forward()) { if (!word_res_it.data()->part_of_combo) { tprintf("%s/%1d ", word_res_it.data()->best_choice->unichar_string().string(), (int)word_res_it.data()->best_choice->permuter()); } } tprintf("\"\n"); } else if (improved) { tprintf("FIX SPACING \"%s\" => \"", stats_.dump_words_str.string()); for (word_res_it.mark_cycle_pt(); !word_res_it.cycled_list(); word_res_it.forward()) { if (!word_res_it.data()->part_of_combo) { tprintf("%s/%1d ", word_res_it.data()->best_choice->unichar_string().string(), (int)word_res_it.data()->best_choice->permuter()); } } tprintf("\"\n"); } } } BOOL8 Tesseract::fixspace_thinks_word_done(WERD_RES *word) { if (word->done) return TRUE; /* Use all the standard pass 2 conditions for mode 5 in set_done() in reject.c BUT DON'T REJECT IF THE WERD IS AMBIGUOUS - FOR SPACING WE DON'T CARE WHETHER WE HAVE of/at on/an etc. */ if (fixsp_done_mode > 0 && (word->tess_accepted || (fixsp_done_mode == 2 && word->reject_map.reject_count() == 0) || fixsp_done_mode == 3) && (strchr(word->best_choice->unichar_string().string(), ' ') == NULL) && ((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))) { return TRUE; } else { return FALSE; } } /** * @name fix_sp_fp_word() * Test the current word to see if it can be split by deleting noise blobs. If * so, do the business. * Return with the iterator pointing to the same place if the word is unchanged, * or the last of the replacement words. */ void Tesseract::fix_sp_fp_word(WERD_RES_IT &word_res_it, ROW *row, BLOCK* block) { WERD_RES *word_res; WERD_RES_LIST sub_word_list; WERD_RES_IT sub_word_list_it(&sub_word_list); inT16 blob_index; inT16 new_length; float junk; word_res = word_res_it.data(); if (word_res->word->flag(W_REP_CHAR) || word_res->combination || word_res->part_of_combo || !word_res->word->flag(W_DONT_CHOP)) return; blob_index = worst_noise_blob(word_res, &junk); if (blob_index < 0) return; if (debug_fix_space_level > 1) { tprintf("FP fixspace working on \"%s\"\n", word_res->best_choice->unichar_string().string()); } word_res->word->rej_cblob_list()->sort(c_blob_comparator); sub_word_list_it.add_after_stay_put(word_res_it.extract()); fix_noisy_space_list(sub_word_list, row, block); new_length = sub_word_list.length(); word_res_it.add_list_before(&sub_word_list); for (; !word_res_it.at_last() && new_length > 1; new_length--) { word_res_it.forward(); } } void Tesseract::fix_noisy_space_list(WERD_RES_LIST &best_perm, ROW *row, BLOCK* block) { inT16 best_score; WERD_RES_IT best_perm_it(&best_perm); WERD_RES_LIST current_perm; WERD_RES_IT current_perm_it(¤t_perm); WERD_RES *old_word_res; inT16 current_score; BOOL8 improved = FALSE; best_score = fp_eval_word_spacing(best_perm); // default score dump_words(best_perm, best_score, 1, improved); 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 current_perm_it.add_to_end(WERD_RES::deep_copy(old_word_res)); old_word_res->combination = FALSE; // Undo kludge break_noisiest_blob_word(current_perm); while (best_score != PERFECT_WERDS && !current_perm.empty()) { match_current_words(current_perm, row, block); current_score = fp_eval_word_spacing(current_perm); dump_words(current_perm, current_score, 2, improved); if (current_score > best_score) { best_perm.clear(); best_perm.deep_copy(¤t_perm, &WERD_RES::deep_copy); best_score = current_score; improved = TRUE; } if (current_score < PERFECT_WERDS) { break_noisiest_blob_word(current_perm); } } dump_words(best_perm, best_score, 3, improved); } /** * break_noisiest_blob_word() * Find the word with the blob which looks like the worst noise. * Break the word into two, deleting the noise blob. */ void Tesseract::break_noisiest_blob_word(WERD_RES_LIST &words) { WERD_RES_IT word_it(&words); WERD_RES_IT worst_word_it; float worst_noise_score = 9999; int worst_blob_index = -1; // Noisiest blob of noisiest wd int blob_index; // of wds noisiest blob float noise_score; // of wds noisiest blob WERD_RES *word_res; C_BLOB_IT blob_it; C_BLOB_IT rej_cblob_it; C_BLOB_LIST new_blob_list; C_BLOB_IT new_blob_it; C_BLOB_IT new_rej_cblob_it; WERD *new_word; inT16 start_of_noise_blob; inT16 i; for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) { blob_index = worst_noise_blob(word_it.data(), &noise_score); if (blob_index > -1 && worst_noise_score > noise_score) { worst_noise_score = noise_score; worst_blob_index = blob_index; worst_word_it = word_it; } } if (worst_blob_index < 0) { words.clear(); // signal termination return; } /* Now split the worst_word_it */ word_res = worst_word_it.data(); /* Move blobs before noise blob to a new bloblist */ new_blob_it.set_to_list(&new_blob_list); blob_it.set_to_list(word_res->word->cblob_list()); for (i = 0; i < worst_blob_index; i++, blob_it.forward()) { new_blob_it.add_after_then_move(blob_it.extract()); } start_of_noise_blob = blob_it.data()->bounding_box().left(); delete blob_it.extract(); // throw out noise blob new_word = new WERD(&new_blob_list, word_res->word); new_word->set_flag(W_EOL, FALSE); word_res->word->set_flag(W_BOL, FALSE); word_res->word->set_blanks(1); // After break new_rej_cblob_it.set_to_list(new_word->rej_cblob_list()); rej_cblob_it.set_to_list(word_res->word->rej_cblob_list()); for (; (!rej_cblob_it.empty() && (rej_cblob_it.data()->bounding_box().left() < start_of_noise_blob)); rej_cblob_it.forward()) { new_rej_cblob_it.add_after_then_move(rej_cblob_it.extract()); } WERD_RES* new_word_res = new WERD_RES(new_word); new_word_res->combination = TRUE; worst_word_it.add_before_then_move(new_word_res); word_res->ClearResults(); } inT16 Tesseract::worst_noise_blob(WERD_RES *word_res, float *worst_noise_score) { float noise_score[512]; int i; int min_noise_blob; // 1st contender int max_noise_blob; // last contender int non_noise_count; int worst_noise_blob; // Worst blob float small_limit = kBlnXHeight * fixsp_small_outlines_size; float non_noise_limit = kBlnXHeight * 0.8; if (word_res->rebuild_word == NULL) return -1; // Can't handle cube words. // Normalised. int blob_count = word_res->box_word->length(); ASSERT_HOST(blob_count <= 512); if (blob_count < 5) return -1; // too short to split /* Get the noise scores for all blobs */ #ifndef SECURE_NAMES if (debug_fix_space_level > 5) tprintf("FP fixspace Noise metrics for \"%s\": ", word_res->best_choice->unichar_string().string()); #endif 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 noise_score[i] = blob_noise_score(blob); if (debug_fix_space_level > 5) tprintf("%1.1f ", noise_score[i]); } if (debug_fix_space_level > 5) tprintf("\n"); /* Now find the worst one which is far enough away from the end of the word */ non_noise_count = 0; for (i = 0; i < blob_count && non_noise_count < fixsp_non_noise_limit; i++) { if (noise_score[i] >= non_noise_limit) { non_noise_count++; } } if (non_noise_count < fixsp_non_noise_limit) return -1; min_noise_blob = i; non_noise_count = 0; for (i = blob_count - 1; i >= 0 && non_noise_count < fixsp_non_noise_limit; i--) { if (noise_score[i] >= non_noise_limit) { non_noise_count++; } } if (non_noise_count < fixsp_non_noise_limit) return -1; max_noise_blob = i; if (min_noise_blob > max_noise_blob) return -1; *worst_noise_score = small_limit; worst_noise_blob = -1; for (i = min_noise_blob; i <= max_noise_blob; i++) { if (noise_score[i] < *worst_noise_score) { worst_noise_blob = i; *worst_noise_score = noise_score[i]; } } return worst_noise_blob; } float Tesseract::blob_noise_score(TBLOB *blob) { TBOX box; // BB of outline inT16 outline_count = 0; inT16 max_dimension; inT16 largest_outline_dimension = 0; for (TESSLINE* ol = blob->outlines; ol != NULL; ol= ol->next) { outline_count++; box = ol->bounding_box(); if (box.height() > box.width()) { max_dimension = box.height(); } else { max_dimension = box.width(); } if (largest_outline_dimension < max_dimension) largest_outline_dimension = max_dimension; } if (outline_count > 5) { // penalise LOTS of blobs largest_outline_dimension *= 2; } box = blob->bounding_box(); if (box.bottom() > kBlnBaselineOffset * 4 || box.top() < kBlnBaselineOffset / 2) { // Lax blob is if high or low largest_outline_dimension /= 2; } return largest_outline_dimension; } } // namespace tesseract void fixspace_dbg(WERD_RES *word) { TBOX box = word->word->bounding_box(); BOOL8 show_map_detail = FALSE; inT16 i; box.print(); tprintf(" \"%s\" ", word->best_choice->unichar_string().string()); tprintf("Blob count: %d (word); %d/%d (rebuild word)\n", word->word->cblob_list()->length(), word->rebuild_word->NumBlobs(), word->box_word->length()); word->reject_map.print(debug_fp); tprintf("\n"); if (show_map_detail) { tprintf("\"%s\"\n", word->best_choice->unichar_string().string()); for (i = 0; word->best_choice->unichar_string()[i] != '\0'; i++) { tprintf("**** \"%c\" ****\n", word->best_choice->unichar_string()[i]); word->reject_map[i].full_print(debug_fp); } } tprintf("Tess Accepted: %s\n", word->tess_accepted ? "TRUE" : "FALSE"); tprintf("Done flag: %s\n\n", word->done ? "TRUE" : "FALSE"); } /** * fp_eval_word_spacing() * Evaluation function for fixed pitch word lists. * * Basically, count the number of "nice" characters - those which are in tess * acceptable words or in dict words and are not rejected. * Penalise any potential noise chars */ namespace tesseract { inT16 Tesseract::fp_eval_word_spacing(WERD_RES_LIST &word_res_list) { WERD_RES_IT word_it(&word_res_list); WERD_RES *word; inT16 score = 0; inT16 i; float small_limit = kBlnXHeight * fixsp_small_outlines_size; for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) { word = word_it.data(); if (word->rebuild_word == NULL) continue; // Can't handle cube words. if (word->done || word->tess_accepted || word->best_choice->permuter() == SYSTEM_DAWG_PERM || word->best_choice->permuter() == FREQ_DAWG_PERM || word->best_choice->permuter() == USER_DAWG_PERM || safe_dict_word(word) > 0) { int num_blobs = word->rebuild_word->NumBlobs(); UNICHAR_ID space = word->uch_set->unichar_to_id(" "); 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 } else if (word->reject_map[i].accepted()) { score++; } } } } if (score < 0) score = 0; return score; } } // namespace tesseract tesseract-3.04.01/ccmain/fixspace.h000066400000000000000000000024271266071204500170710ustar00rootroot00000000000000/****************************************************************** * File: fixspace.h (Formerly fixspace.h) * Description: Implements a pass over the page res, exploring the alternative * spacing possibilities, trying to use context to improve the word spacing * Author: Phil Cheatle * Created: Thu Oct 21 11:38:43 BST 1993 * * (C) Copyright 1993, 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 FIXSPACE_H #define FIXSPACE_H #include "pageres.h" #include "params.h" void initialise_search(WERD_RES_LIST &src_list, WERD_RES_LIST &new_list); void transform_to_next_perm(WERD_RES_LIST &words); void fixspace_dbg(WERD_RES *word); #endif tesseract-3.04.01/ccmain/fixxht.cpp000066400000000000000000000241351266071204500171340ustar00rootroot00000000000000/********************************************************************** * File: fixxht.cpp (Formerly fixxht.c) * Description: Improve x_ht and look out for case inconsistencies * Author: Phil Cheatle * Created: Thu Aug 5 14:11:08 BST 1993 * * (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. * **********************************************************************/ #include #include #include "params.h" #include "float2int.h" #include "tesseractclass.h" namespace tesseract { // Fixxht overview. // Premise: Initial estimate of x-height is adequate most of the time, but // occasionally it is incorrect. Most notable causes of failure are: // 1. Small caps, where the top of the caps is the same as the body text // xheight. For small caps words the xheight needs to be reduced to correctly // recognize the caps in the small caps word. // 2. All xheight lines, such as summer. Here the initial estimate will have // guessed that the blob tops are caps and will have placed the xheight too low. // 3. Noise/logos beside words, or changes in font size on a line. Such // things can blow the statistics and cause an incorrect estimate. // 4. Incorrect baseline. Can happen when 2 columns are incorrectly merged. // In this case the x-height is often still correct. // // Algorithm. // Compare the vertical position (top only) of alphnumerics in a word with // the range of positions in training data (in the unicharset). // See CountMisfitTops. If any characters disagree sufficiently with the // initial xheight estimate, then recalculate the xheight, re-run OCR on // the word, and if the number of vertical misfits goes down, along with // either the word rating or certainty, then keep the new xheight. // The new xheight is calculated as follows:ComputeCompatibleXHeight // For each alphanumeric character that has a vertically misplaced top // (a misfit), yet its bottom is within the acceptable range (ie it is not // likely a sub-or super-script) calculate the range of acceptable xheight // positions from its range of tops, and give each value in the range a // number of votes equal to the distance of its top from its acceptance range. // The x-height position with the median of the votes becomes the new // x-height. This assumes that most characters will be correctly recognized // even if the x-height is incorrect. This is not a terrible assumption, but // it is not great. An improvement would be to use a classifier that does // not care about vertical position or scaling at all. // Separately collect stats on shifted baselines and apply the same logic to // computing a best-fit shift to fix the error. If the baseline needs to be // shifted, but the x-height is OK, returns the original x-height along with // the baseline shift to indicate that recognition needs to re-run. // If the max-min top of a unicharset char is bigger than kMaxCharTopRange // then the char top cannot be used to judge misfits or suggest a new top. 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; 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(); if (top >= INT_FEAT_RANGE) top = INT_FEAT_RANGE - 1; int min_bottom, max_bottom, min_top, max_top; unicharset.get_top_bottom(class_id, &min_bottom, &max_bottom, &min_top, &max_top); if (max_top - min_top > kMaxCharTopRange) continue; bool bad = top < min_top - x_ht_acceptance_tolerance || top > max_top + x_ht_acceptance_tolerance; if (bad) ++bad_blobs; if (debug_x_ht_level >= 1) { tprintf("Class %s is %s with top %d vs limits of %d->%d, +/-%d\n", unicharset.id_to_unichar(class_id), bad ? "Misfit" : "OK", top, min_top, max_top, static_cast(x_ht_acceptance_tolerance)); } } } return bad_blobs; } // Returns a new x-height maximally compatible with the result in word_res. // See comment above for overall algorithm. float Tesseract::ComputeCompatibleXheight(WERD_RES *word_res, float* baseline_shift) { STATS top_stats(0, MAX_UINT8); STATS shift_stats(-MAX_UINT8, MAX_UINT8); int bottom_shift = 0; int num_blobs = word_res->rebuild_word->NumBlobs(); do { top_stats.clear(); shift_stats.clear(); 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() + bottom_shift; // Clip the top to the limit of normalized feature space. if (top >= INT_FEAT_RANGE) top = INT_FEAT_RANGE - 1; int bottom = blob->bounding_box().bottom() + bottom_shift; int min_bottom, max_bottom, min_top, max_top; unicharset.get_top_bottom(class_id, &min_bottom, &max_bottom, &min_top, &max_top); // Chars with a wild top range would mess up the result so ignore them. if (max_top - min_top > kMaxCharTopRange) continue; int misfit_dist = MAX((min_top - x_ht_acceptance_tolerance) - top, top - (max_top + x_ht_acceptance_tolerance)); int height = top - kBlnBaselineOffset; if (debug_x_ht_level >= 2) { tprintf("Class %s: height=%d, bottom=%d,%d top=%d,%d, actual=%d,%d: ", unicharset.id_to_unichar(class_id), height, min_bottom, max_bottom, min_top, max_top, bottom, top); } // Use only chars that fit in the expected bottom range, and where // the range of tops is sensibly near the xheight. if (min_bottom <= bottom + x_ht_acceptance_tolerance && bottom - x_ht_acceptance_tolerance <= max_bottom && min_top > kBlnBaselineOffset && max_top - kBlnBaselineOffset >= kBlnXHeight && misfit_dist > 0) { // Compute the x-height position using proportionality between the // actual height and expected height. int min_xht = DivRounded(height * kBlnXHeight, max_top - kBlnBaselineOffset); int max_xht = DivRounded(height * kBlnXHeight, min_top - kBlnBaselineOffset); if (debug_x_ht_level >= 2) { tprintf(" xht range min=%d, max=%d\n", min_xht, max_xht); } // The range of expected heights gets a vote equal to the distance // of the actual top from the expected top. for (int y = min_xht; y <= max_xht; ++y) top_stats.add(y, misfit_dist); } else if ((min_bottom > bottom + x_ht_acceptance_tolerance || bottom - x_ht_acceptance_tolerance > max_bottom) && bottom_shift == 0) { // Get the range of required bottom shift. int min_shift = min_bottom - bottom; int max_shift = max_bottom - bottom; if (debug_x_ht_level >= 2) { tprintf(" bottom shift min=%d, max=%d\n", min_shift, max_shift); } // The range of expected shifts gets a vote equal to the min distance // of the actual bottom from the expected bottom, spread over the // range of its acceptance. int misfit_weight = abs(min_shift); if (max_shift > min_shift) misfit_weight /= max_shift - min_shift; for (int y = min_shift; y <= max_shift; ++y) shift_stats.add(y, misfit_weight); } else { if (bottom_shift == 0) { // Things with bottoms that are already ok need to say so, on the // 1st iteration only. shift_stats.add(0, kBlnBaselineOffset); } if (debug_x_ht_level >= 2) { tprintf(" already OK\n"); } } } } if (shift_stats.get_total() > top_stats.get_total()) { bottom_shift = IntCastRounded(shift_stats.median()); if (debug_x_ht_level >= 2) { tprintf("Applying bottom shift=%d\n", bottom_shift); } } } while (bottom_shift != 0 && top_stats.get_total() < shift_stats.get_total()); // Baseline shift is opposite sign to the bottom shift. *baseline_shift = -bottom_shift / word_res->denorm.y_scale(); if (debug_x_ht_level >= 2) { tprintf("baseline shift=%g\n", *baseline_shift); } if (top_stats.get_total() == 0) return bottom_shift != 0 ? word_res->x_height : 0.0f; // The new xheight is just the median vote, which is then scaled out // of BLN space back to pixel space to get the x-height in pixel space. float new_xht = top_stats.median(); if (debug_x_ht_level >= 2) { tprintf("Median xht=%f\n", new_xht); tprintf("Mode20:A: New x-height = %f (norm), %f (orig)\n", new_xht, new_xht / word_res->denorm.y_scale()); } // The xheight must change by at least x_ht_min_change to be used. if (fabs(new_xht - kBlnXHeight) >= x_ht_min_change) return new_xht / word_res->denorm.y_scale(); else return bottom_shift != 0 ? word_res->x_height : 0.0f; } } // namespace tesseract tesseract-3.04.01/ccmain/ltrresultiterator.cpp000066400000000000000000000353411266071204500214350ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: ltrresultiterator.cpp // Description: Iterator for tesseract results in strict left-to-right // order that avoids using tesseract internal data structures. // Author: Ray Smith // Created: Fri Feb 26 14:32:09 PST 2010 // // (C) Copyright 2010, 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 "ltrresultiterator.h" #include "allheaders.h" #include "pageres.h" #include "strngs.h" #include "tesseractclass.h" namespace tesseract { LTRResultIterator::LTRResultIterator(PAGE_RES* page_res, Tesseract* tesseract, int scale, int scaled_yres, int rect_left, int rect_top, int rect_width, int rect_height) : PageIterator(page_res, tesseract, scale, scaled_yres, rect_left, rect_top, rect_width, rect_height), line_separator_("\n"), paragraph_separator_("\n") { } LTRResultIterator::~LTRResultIterator() { } // Returns the null terminated UTF-8 encoded text string for the current // object at the given level. Use delete [] to free after use. char* LTRResultIterator::GetUTF8Text(PageIteratorLevel level) const { if (it_->word() == NULL) return NULL; // Already at the end! STRING text; PAGE_RES_IT res_it(*it_); WERD_CHOICE* best_choice = res_it.word()->best_choice; ASSERT_HOST(best_choice != NULL); if (level == RIL_SYMBOL) { text = res_it.word()->BestUTF8(blob_index_, false); } else if (level == RIL_WORD) { text = best_choice->unichar_string(); } else { bool eol = false; // end of line? bool eop = false; // end of paragraph? do { // for each paragraph in a block do { // for each text line in a paragraph do { // for each word in a text line best_choice = res_it.word()->best_choice; ASSERT_HOST(best_choice != NULL); text += best_choice->unichar_string(); text += " "; res_it.forward(); eol = res_it.row() != res_it.prev_row(); } while (!eol); text.truncate_at(text.length() - 1); text += line_separator_; eop = res_it.block() != res_it.prev_block() || res_it.row()->row->para() != res_it.prev_row()->row->para(); } while (level != RIL_TEXTLINE && !eop); if (eop) text += paragraph_separator_; } while (level == RIL_BLOCK && res_it.block() == res_it.prev_block()); } int length = text.length() + 1; char* result = new char[length]; strncpy(result, text.string(), length); return result; } // Set the string inserted at the end of each text line. "\n" by default. void LTRResultIterator::SetLineSeparator(const char *new_line) { line_separator_ = new_line; } // Set the string inserted at the end of each paragraph. "\n" by default. void LTRResultIterator::SetParagraphSeparator(const char *new_para) { paragraph_separator_ = new_para; } // Returns the mean confidence of the current object at the given level. // The number should be interpreted as a percent probability. (0.0f-100.0f) float LTRResultIterator::Confidence(PageIteratorLevel level) const { if (it_->word() == NULL) return 0.0f; // Already at the end! float mean_certainty = 0.0f; int certainty_count = 0; PAGE_RES_IT res_it(*it_); WERD_CHOICE* best_choice = res_it.word()->best_choice; ASSERT_HOST(best_choice != NULL); switch (level) { case RIL_BLOCK: do { best_choice = res_it.word()->best_choice; ASSERT_HOST(best_choice != NULL); mean_certainty += best_choice->certainty(); ++certainty_count; res_it.forward(); } while (res_it.block() == res_it.prev_block()); break; case RIL_PARA: do { best_choice = res_it.word()->best_choice; ASSERT_HOST(best_choice != NULL); mean_certainty += best_choice->certainty(); ++certainty_count; res_it.forward(); } while (res_it.block() == res_it.prev_block() && res_it.row()->row->para() == res_it.prev_row()->row->para()); break; case RIL_TEXTLINE: do { best_choice = res_it.word()->best_choice; ASSERT_HOST(best_choice != NULL); mean_certainty += best_choice->certainty(); ++certainty_count; res_it.forward(); } while (res_it.row() == res_it.prev_row()); break; case RIL_WORD: mean_certainty += best_choice->certainty(); ++certainty_count; break; case RIL_SYMBOL: mean_certainty += best_choice->certainty(blob_index_); ++certainty_count; } if (certainty_count > 0) { mean_certainty /= certainty_count; float confidence = 100 + 5 * mean_certainty; if (confidence < 0.0f) confidence = 0.0f; if (confidence > 100.0f) confidence = 100.0f; return confidence; } return 0.0f; } void LTRResultIterator::RowAttributes(float* row_height, float* descenders, float* ascenders) const { *row_height = it_->row()->row->x_height() + it_->row()-> row->ascenders() - it_->row()->row->descenders(); *descenders = it_->row()->row->descenders(); *ascenders = it_->row()->row->ascenders(); } // Returns the font attributes of the current word. If iterating at a higher // level object than words, eg textlines, then this will return the // attributes of the first word in that textline. // The actual return value is a string representing a font name. It points // to an internal table and SHOULD NOT BE DELETED. Lifespan is the same as // the iterator itself, ie rendered invalid by various members of // TessBaseAPI, including Init, SetImage, End or deleting the TessBaseAPI. // Pointsize is returned in printers points (1/72 inch.) const char* LTRResultIterator::WordFontAttributes(bool* is_bold, bool* is_italic, bool* is_underlined, bool* is_monospace, bool* is_serif, bool* is_smallcaps, int* pointsize, int* font_id) const { if (it_->word() == NULL) return NULL; // Already at the end! if (it_->word()->fontinfo == NULL) { *font_id = -1; return NULL; // No font information. } const FontInfo& font_info = *it_->word()->fontinfo; *font_id = font_info.universal_id; *is_bold = font_info.is_bold(); *is_italic = font_info.is_italic(); *is_underlined = false; // TODO(rays) fix this! *is_monospace = font_info.is_fixed_pitch(); *is_serif = font_info.is_serif(); *is_smallcaps = it_->word()->small_caps; float row_height = it_->row()->row->x_height() + it_->row()->row->ascenders() - it_->row()->row->descenders(); // Convert from pixels to printers points. *pointsize = scaled_yres_ > 0 ? static_cast(row_height * kPointsPerInch / scaled_yres_ + 0.5) : 0; return font_info.name; } // Returns the name of the language used to recognize this word. const char* LTRResultIterator::WordRecognitionLanguage() const { if (it_->word() == NULL || it_->word()->tesseract == NULL) return NULL; return it_->word()->tesseract->lang.string(); } // Return the overall directionality of this word. StrongScriptDirection LTRResultIterator::WordDirection() const { if (it_->word() == NULL) return DIR_NEUTRAL; bool has_rtl = it_->word()->AnyRtlCharsInWord(); bool has_ltr = it_->word()->AnyLtrCharsInWord(); if (has_rtl && !has_ltr) return DIR_RIGHT_TO_LEFT; if (has_ltr && !has_rtl) return DIR_LEFT_TO_RIGHT; if (!has_ltr && !has_rtl) return DIR_NEUTRAL; return DIR_MIX; } // Returns true if the current word was found in a dictionary. bool LTRResultIterator::WordIsFromDictionary() const { if (it_->word() == NULL) return false; // Already at the end! int permuter = it_->word()->best_choice->permuter(); return permuter == SYSTEM_DAWG_PERM || permuter == FREQ_DAWG_PERM || permuter == USER_DAWG_PERM; } // Returns true if the current word is numeric. bool LTRResultIterator::WordIsNumeric() const { if (it_->word() == NULL) return false; // Already at the end! int permuter = it_->word()->best_choice->permuter(); return permuter == NUMBER_PERM; } // 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->HasDebugInfo(); } // Returns the pointer to ParamsTrainingBundle stored in the BlamerBundle // of the current word. const void *LTRResultIterator::GetParamsTrainingBundle() const { return (it_->word() != NULL && it_->word()->blamer_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(); } // 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(); } // 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 (!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(); } // Returns true if the current symbol is a superscript. // If iterating at a higher level object than symbols, eg words, then // 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()->best_choice->BlobPosition(blob_index_) == SP_SUPERSCRIPT; return false; } // Returns true if the current symbol is a subscript. // If iterating at a higher level object than symbols, eg words, then // 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()->best_choice->BlobPosition(blob_index_) == SP_SUBSCRIPT; return false; } // Returns true if the current symbol is a dropcap. // If iterating at a higher level object than symbols, eg words, then // 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()->best_choice->BlobPosition(blob_index_) == SP_DROPCAP; return false; } ChoiceIterator::ChoiceIterator(const LTRResultIterator& result_it) { ASSERT_HOST(result_it.it_->word() != NULL); word_res_ = result_it.it_->word(); BLOB_CHOICE_LIST* choices = NULL; if (word_res_->ratings != NULL) choices = word_res_->GetBlobChoices(result_it.blob_index_); if (choices != NULL && !choices->empty()) { choice_it_ = new BLOB_CHOICE_IT(choices); choice_it_->mark_cycle_pt(); } else { choice_it_ = NULL; } } ChoiceIterator::~ChoiceIterator() { delete choice_it_; } // Moves to the next choice for the symbol and returns false if there // are none left. bool ChoiceIterator::Next() { if (choice_it_ == NULL) return false; choice_it_->forward(); return !choice_it_->cycled_list(); } // Returns the null terminated UTF-8 encoded text string for the current // choice. Do NOT use delete [] to free after use. const char* ChoiceIterator::GetUTF8Text() const { if (choice_it_ == NULL) return NULL; UNICHAR_ID id = choice_it_->data()->unichar_id(); return word_res_->uch_set->id_to_unichar_ext(id); } // Returns the confidence of the current choice. // The number should be interpreted as a percent probability. (0.0f-100.0f) float ChoiceIterator::Confidence() const { if (choice_it_ == NULL) return 0.0f; float confidence = 100 + 5 * choice_it_->data()->certainty(); if (confidence < 0.0f) confidence = 0.0f; if (confidence > 100.0f) confidence = 100.0f; return confidence; } } // namespace tesseract. tesseract-3.04.01/ccmain/ltrresultiterator.h000066400000000000000000000223021266071204500210730ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: ltrresultiterator.h // Description: Iterator for tesseract results in strict left-to-right // order that avoids using tesseract internal data structures. // Author: Ray Smith // Created: Fri Feb 26 11:01:06 PST 2010 // // (C) Copyright 2010, 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_CCMAIN_LTR_RESULT_ITERATOR_H__ #define TESSERACT_CCMAIN_LTR_RESULT_ITERATOR_H__ #include "platform.h" #include "pageiterator.h" #include "unichar.h" class BLOB_CHOICE_IT; class WERD_RES; namespace tesseract { class Tesseract; // Class to iterate over tesseract results, providing access to all levels // of the page hierarchy, without including any tesseract headers or having // to handle any tesseract structures. // WARNING! This class points to data held within the TessBaseAPI class, and // therefore can only be used while the TessBaseAPI class still exists and // has not been subjected to a call of Init, SetImage, Recognize, Clear, End // DetectOS, or anything else that changes the internal PAGE_RES. // See apitypes.h for the definition of PageIteratorLevel. // See also base class PageIterator, which contains the bulk of the interface. // LTRResultIterator adds text-specific methods for access to OCR output. class TESS_API LTRResultIterator : public PageIterator { friend class ChoiceIterator; public: // page_res and tesseract come directly from the BaseAPI. // The rectangle parameters are copied indirectly from the Thresholder, // via the BaseAPI. They represent the coordinates of some rectangle in an // original image (in top-left-origin coordinates) and therefore the top-left // needs to be added to any output boxes in order to specify coordinates // in the original image. See TessBaseAPI::SetRectangle. // The scale and scaled_yres are in case the Thresholder scaled the image // rectangle prior to thresholding. Any coordinates in tesseract's image // must be divided by scale before adding (rect_left, rect_top). // The scaled_yres indicates the effective resolution of the binary image // that tesseract has been given by the Thresholder. // After the constructor, Begin has already been called. LTRResultIterator(PAGE_RES* page_res, Tesseract* tesseract, int scale, int scaled_yres, int rect_left, int rect_top, int rect_width, int rect_height); virtual ~LTRResultIterator(); // LTRResultIterators may be copied! This makes it possible to iterate over // all the objects at a lower level, while maintaining an iterator to // objects at a higher level. These constructors DO NOT CALL Begin, so // iterations will continue from the location of src. // TODO: For now the copy constructor and operator= only need the base class // versions, but if new data members are added, don't forget to add them! // ============= Moving around within the page ============. // See PageIterator. // ============= Accessing data ==============. // Returns the null terminated UTF-8 encoded text string for the current // object at the given level. Use delete [] to free after use. char* GetUTF8Text(PageIteratorLevel level) const; // Set the string inserted at the end of each text line. "\n" by default. void SetLineSeparator(const char *new_line); // Set the string inserted at the end of each paragraph. "\n" by default. void SetParagraphSeparator(const char *new_para); // Returns the mean confidence of the current object at the given level. // The number should be interpreted as a percent probability. (0.0f-100.0f) float Confidence(PageIteratorLevel level) const; // Returns the attributes of the current row. void RowAttributes(float* row_height, float* descenders, float* ascenders) const; // ============= Functions that refer to words only ============. // Returns the font attributes of the current word. If iterating at a higher // level object than words, eg textlines, then this will return the // attributes of the first word in that textline. // The actual return value is a string representing a font name. It points // to an internal table and SHOULD NOT BE DELETED. Lifespan is the same as // the iterator itself, ie rendered invalid by various members of // TessBaseAPI, including Init, SetImage, End or deleting the TessBaseAPI. // Pointsize is returned in printers points (1/72 inch.) const char* WordFontAttributes(bool* is_bold, bool* is_italic, bool* is_underlined, bool* is_monospace, bool* is_serif, bool* is_smallcaps, int* pointsize, int* font_id) const; // Return the name of the language used to recognize this word. // On error, NULL. Do not delete this pointer. const char* WordRecognitionLanguage() const; // Return the overall directionality of this word. StrongScriptDirection WordDirection() const; // Returns true if the current word was found in a dictionary. bool WordIsFromDictionary() const; // Returns true if the current word is numeric. bool WordIsNumeric() const; // Returns true if the word contains blamer information. bool HasBlamerInfo() const; // Returns the pointer to ParamsTrainingBundle stored in the BlamerBundle // of the current word. 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. const char *GetBlamerDebug() const; // Returns a pointer to the string with misadaption information for this word. // 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; // ============= Functions that refer to symbols only ============. // Returns true if the current symbol is a superscript. // If iterating at a higher level object than symbols, eg words, then // this will return the attributes of the first symbol in that word. bool SymbolIsSuperscript() const; // Returns true if the current symbol is a subscript. // If iterating at a higher level object than symbols, eg words, then // this will return the attributes of the first symbol in that word. bool SymbolIsSubscript() const; // Returns true if the current symbol is a dropcap. // If iterating at a higher level object than symbols, eg words, then // this will return the attributes of the first symbol in that word. bool SymbolIsDropcap() const; protected: const char *line_separator_; const char *paragraph_separator_; }; // Class to iterate over the classifier choices for a single RIL_SYMBOL. class ChoiceIterator { public: // Construction is from a LTRResultIterator that points to the symbol of // interest. The ChoiceIterator allows a one-shot iteration over the // choices for this symbol and after that is is useless. explicit ChoiceIterator(const LTRResultIterator& result_it); ~ChoiceIterator(); // Moves to the next choice for the symbol and returns false if there // are none left. bool Next(); // ============= Accessing data ==============. // Returns the null terminated UTF-8 encoded text string for the current // choice. // NOTE: Unlike LTRResultIterator::GetUTF8Text, the return points to an // internal structure and should NOT be delete[]ed to free after use. const char* GetUTF8Text() const; // Returns the confidence of the current choice. // The number should be interpreted as a percent probability. (0.0f-100.0f) float Confidence() const; private: // Pointer to the WERD_RES object owned by the API. WERD_RES* word_res_; // Iterator over the blob choices. BLOB_CHOICE_IT* choice_it_; }; } // namespace tesseract. #endif // TESSERACT_CCMAIN_LTR_RESULT_ITERATOR_H__ tesseract-3.04.01/ccmain/mutableiterator.h000066400000000000000000000051571266071204500204750ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: mutableiterator.h // Description: Iterator for tesseract results providing access to // both high-level API and Tesseract internal data structures. // Author: David Eger // Created: Thu Feb 24 19:01:06 PST 2011 // // (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_CCMAIN_MUTABLEITERATOR_H__ #define TESSERACT_CCMAIN_MUTABLEITERATOR_H__ #include "resultiterator.h" class BLOB_CHOICE_IT; namespace tesseract { class Tesseract; // Class to iterate over tesseract results, providing access to all levels // of the page hierarchy, without including any tesseract headers or having // to handle any tesseract structures. // WARNING! This class points to data held within the TessBaseAPI class, and // therefore can only be used while the TessBaseAPI class still exists and // has not been subjected to a call of Init, SetImage, Recognize, Clear, End // DetectOS, or anything else that changes the internal PAGE_RES. // See apitypes.h for the definition of PageIteratorLevel. // See also base class PageIterator, which contains the bulk of the interface. // ResultIterator adds text-specific methods for access to OCR output. // MutableIterator adds access to internal data structures. class MutableIterator : public ResultIterator { public: // See argument descriptions in ResultIterator() MutableIterator(PAGE_RES* page_res, Tesseract* tesseract, int scale, int scaled_yres, int rect_left, int rect_top, int rect_width, int rect_height) : ResultIterator( LTRResultIterator(page_res, tesseract, scale, scaled_yres, rect_left, rect_top, rect_width, rect_height)) {} virtual ~MutableIterator() {} // See PageIterator and ResultIterator for most calls. // Return access to Tesseract internals. const PAGE_RES_IT *PageResIt() const { return it_; } }; } // namespace tesseract. #endif // TESSERACT_CCMAIN_MUTABLEITERATOR_H__ tesseract-3.04.01/ccmain/osdetect.cpp000066400000000000000000000471671266071204500174460ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: osdetect.cpp // Description: Orientation and script detection. // Author: Samuel Charron // Ranjith Unnikrishnan // // (C) Copyright 2008, 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 "osdetect.h" #include "blobbox.h" #include "blread.h" #include "colfind.h" #include "fontinfo.h" #include "imagefind.h" #include "linefind.h" #include "oldlist.h" #include "qrsequence.h" #include "ratngs.h" #include "strngs.h" #include "tabvector.h" #include "tesseractclass.h" #include "textord.h" const int kMinCharactersToTry = 50; const int kMaxCharactersToTry = 5 * kMinCharactersToTry; const float kSizeRatioToReject = 2.0; const int kMinAcceptableBlobHeight = 10; const float kOrientationAcceptRatio = 1.3; const float kScriptAcceptRatio = 1.3; const float kHanRatioInKorean = 0.7; const float kHanRatioInJapanese = 0.3; const float kNonAmbiguousMargin = 1.0; // General scripts static const char* han_script = "Han"; static const char* latin_script = "Latin"; static const char* katakana_script = "Katakana"; static const char* hiragana_script = "Hiragana"; static const char* hangul_script = "Hangul"; // Pseudo-scripts Name const char* ScriptDetector::korean_script_ = "Korean"; const char* ScriptDetector::japanese_script_ = "Japanese"; const char* ScriptDetector::fraktur_script_ = "Fraktur"; // Minimum believable resolution. const int kMinCredibleResolution = 70; // Default resolution used if input is not believable. const int kDefaultResolution = 300; void OSResults::update_best_orientation() { float first = orientations[0]; float second = orientations[1]; best_result.orientation_id = 0; if (orientations[0] < orientations[1]) { first = orientations[1]; second = orientations[0]; best_result.orientation_id = 1; } for (int i = 2; i < 4; ++i) { if (orientations[i] > first) { second = first; first = orientations[i]; best_result.orientation_id = i; } else if (orientations[i] > second) { second = orientations[i]; } } // Store difference of top two orientation scores. best_result.oconfidence = first - second; } void OSResults::set_best_orientation(int orientation_id) { best_result.orientation_id = orientation_id; best_result.oconfidence = 0; } void OSResults::update_best_script(int orientation) { // We skip index 0 to ignore the "Common" script. float first = scripts_na[orientation][1]; float second = scripts_na[orientation][2]; best_result.script_id = 1; if (scripts_na[orientation][1] < scripts_na[orientation][2]) { first = scripts_na[orientation][2]; second = scripts_na[orientation][1]; best_result.script_id = 2; } for (int i = 3; i < kMaxNumberOfScripts; ++i) { if (scripts_na[orientation][i] > first) { best_result.script_id = i; second = first; first = scripts_na[orientation][i]; } else if (scripts_na[orientation][i] > second) { second = scripts_na[orientation][i]; } } best_result.sconfidence = (first / second - 1.0) / (kScriptAcceptRatio - 1.0); } int OSResults::get_best_script(int orientation_id) const { int max_id = -1; for (int j = 0; j < kMaxNumberOfScripts; ++j) { const char *script = unicharset->get_script_from_script_id(j); if (strcmp(script, "Common") && strcmp(script, "NULL")) { if (max_id == -1 || scripts_na[orientation_id][j] > scripts_na[orientation_id][max_id]) max_id = j; } } return max_id; } // Print the script scores for all possible orientations. void OSResults::print_scores(void) const { for (int i = 0; i < 4; ++i) { tprintf("Orientation id #%d", i); print_scores(i); } } // Print the script scores for the given candidate orientation. void OSResults::print_scores(int orientation_id) const { for (int j = 0; j < kMaxNumberOfScripts; ++j) { if (scripts_na[orientation_id][j]) { tprintf("%12s\t: %f\n", unicharset->get_script_from_script_id(j), scripts_na[orientation_id][j]); } } } // Accumulate scores with given OSResults instance and update the best script. void OSResults::accumulate(const OSResults& osr) { for (int i = 0; i < 4; ++i) { orientations[i] += osr.orientations[i]; for (int j = 0; j < kMaxNumberOfScripts; ++j) scripts_na[i][j] += osr.scripts_na[i][j]; } unicharset = osr.unicharset; update_best_orientation(); update_best_script(best_result.orientation_id); } // Detect and erase horizontal/vertical lines and picture regions from the // image, so that non-text blobs are removed from consideration. void remove_nontext_regions(tesseract::Tesseract *tess, BLOCK_LIST *blocks, TO_BLOCK_LIST *to_blocks) { Pix *pix = tess->pix_binary(); ASSERT_HOST(pix != NULL); int vertical_x = 0; int vertical_y = 1; tesseract::TabVector_LIST v_lines; tesseract::TabVector_LIST h_lines; const int kMinCredibleResolution = 70; int resolution = (kMinCredibleResolution > pixGetXRes(pix)) ? kMinCredibleResolution : pixGetXRes(pix); tesseract::LineFinder::FindAndRemoveLines(resolution, false, pix, &vertical_x, &vertical_y, NULL, &v_lines, &h_lines); Pix* im_pix = tesseract::ImageFind::FindImages(pix); if (im_pix != NULL) { pixSubtract(pix, pix, im_pix); pixDestroy(&im_pix); } tess->mutable_textord()->find_components(tess->pix_binary(), blocks, to_blocks); } // Find connected components in the page and process a subset until finished or // a stopping criterion is met. // Returns the number of blobs used in making the estimate. 0 implies failure. int orientation_and_script_detection(STRING& filename, OSResults* osr, tesseract::Tesseract* tess) { STRING name = filename; //truncated name const char *lastdot; //of name TBOX page_box; lastdot = strrchr (name.string (), '.'); if (lastdot != NULL) name[lastdot-name.string()] = '\0'; ASSERT_HOST(tess->pix_binary() != NULL) int width = pixGetWidth(tess->pix_binary()); int height = pixGetHeight(tess->pix_binary()); BLOCK_LIST blocks; if (!read_unlv_file(name, width, height, &blocks)) FullPageBlock(width, height, &blocks); // Try to remove non-text regions from consideration. TO_BLOCK_LIST land_blocks, port_blocks; remove_nontext_regions(tess, &blocks, &port_blocks); if (port_blocks.empty()) { // page segmentation did not succeed, so we need to find_components first. tess->mutable_textord()->find_components(tess->pix_binary(), &blocks, &port_blocks); } else { page_box.set_left(0); page_box.set_bottom(0); page_box.set_right(width); page_box.set_top(height); // Filter_blobs sets up the TO_BLOCKs the same as find_components does. tess->mutable_textord()->filter_blobs(page_box.topright(), &port_blocks, true); } return os_detect(&port_blocks, osr, tess); } // Filter and sample the blobs. // Returns a non-zero number of blobs if the page was successfully processed, or // zero if the page had too few characters to be reliable int os_detect(TO_BLOCK_LIST* port_blocks, OSResults* osr, tesseract::Tesseract* tess) { int blobs_total = 0; TO_BLOCK_IT block_it; block_it.set_to_list(port_blocks); BLOBNBOX_CLIST filtered_list; BLOBNBOX_C_IT filtered_it(&filtered_list); for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward ()) { TO_BLOCK* to_block = block_it.data(); if (to_block->block->poly_block() && !to_block->block->poly_block()->IsText()) continue; BLOBNBOX_IT bbox_it; bbox_it.set_to_list(&to_block->blobs); for (bbox_it.mark_cycle_pt (); !bbox_it.cycled_list (); bbox_it.forward ()) { BLOBNBOX* bbox = bbox_it.data(); C_BLOB* blob = bbox->cblob(); TBOX box = blob->bounding_box(); ++blobs_total; float y_x = fabs((box.height() * 1.0) / box.width()); float x_y = 1.0f / y_x; // Select a >= 1.0 ratio float ratio = x_y > y_x ? x_y : y_x; // Blob is ambiguous if (ratio > kSizeRatioToReject) continue; if (box.height() < kMinAcceptableBlobHeight) continue; filtered_it.add_to_end(bbox); } } return os_detect_blobs(NULL, &filtered_list, osr, tess); } // Detect orientation and script from a list of blobs. // Returns a non-zero number of blobs if the list was successfully processed, or // zero if the list had too few characters to be reliable. // If allowed_scripts is non-null and non-empty, it is a list of scripts that // constrains both orientation and script detection to consider only scripts // from the list. int os_detect_blobs(const GenericVector* allowed_scripts, BLOBNBOX_CLIST* blob_list, OSResults* osr, tesseract::Tesseract* tess) { OSResults osr_; if (osr == NULL) osr = &osr_; osr->unicharset = &tess->unicharset; OrientationDetector o(allowed_scripts, osr); ScriptDetector s(allowed_scripts, osr, tess); BLOBNBOX_C_IT filtered_it(blob_list); int real_max = MIN(filtered_it.length(), kMaxCharactersToTry); // tprintf("Total blobs found = %d\n", blobs_total); // tprintf("Number of blobs post-filtering = %d\n", filtered_it.length()); // tprintf("Number of blobs to try = %d\n", real_max); // If there are too few characters, skip this page entirely. if (real_max < kMinCharactersToTry / 2) { tprintf("Too few characters. Skipping this page\n"); return 0; } BLOBNBOX** blobs = new BLOBNBOX*[filtered_it.length()]; int number_of_blobs = 0; for (filtered_it.mark_cycle_pt (); !filtered_it.cycled_list (); filtered_it.forward ()) { blobs[number_of_blobs++] = (BLOBNBOX*)filtered_it.data(); } QRSequenceGenerator sequence(number_of_blobs); int num_blobs_evaluated = 0; for (int i = 0; i < real_max; ++i) { if (os_detect_blob(blobs[sequence.GetVal()], &o, &s, osr, tess) && i > kMinCharactersToTry) { break; } ++num_blobs_evaluated; } delete [] blobs; // Make sure the best_result is up-to-date int orientation = o.get_orientation(); osr->update_best_script(orientation); return num_blobs_evaluated; } // Processes a single blob to estimate script and orientation. // Return true if estimate of orientation and script satisfies stopping // criteria. bool os_detect_blob(BLOBNBOX* bbox, OrientationDetector* o, ScriptDetector* s, OSResults* osr, tesseract::Tesseract* tess) { tess->tess_cn_matching.set_value(true); // turn it on tess->tess_bn_matching.set_value(false); C_BLOB* blob = bbox->cblob(); TBLOB* tblob = TBLOB::PolygonalCopy(tess->poly_allow_detailed_fx, blob); TBOX box = tblob->bounding_box(); FCOORD current_rotation(1.0f, 0.0f); FCOORD rotation90(0.0f, 1.0f); BLOB_CHOICE_LIST ratings[4]; // Test the 4 orientations for (int i = 0; i < 4; ++i) { // Normalize the blob. Set the origin to the place we want to be the // bottom-middle after rotation. // Scaling is to make the rotated height the x-height. float scaling = static_cast(kBlnXHeight) / box.height(); float x_origin = (box.left() + box.right()) / 2.0f; float y_origin = (box.bottom() + box.top()) / 2.0f; if (i == 0 || i == 2) { // Rotation is 0 or 180. y_origin = i == 0 ? box.bottom() : box.top(); } else { // Rotation is 90 or 270. scaling = static_cast(kBlnXHeight) / box.width(); x_origin = i == 1 ? box.left() : box.right(); } TBLOB* rotated_blob = new TBLOB(*tblob); rotated_blob->Normalize(NULL, ¤t_rotation, NULL, x_origin, y_origin, scaling, scaling, 0.0f, static_cast(kBlnBaselineOffset), false, NULL); tess->AdaptiveClassifier(rotated_blob, ratings + i); delete rotated_blob; current_rotation.rotate(rotation90); } delete tblob; bool stop = o->detect_blob(ratings); s->detect_blob(ratings); int orientation = o->get_orientation(); stop = s->must_stop(orientation) && stop; return stop; } OrientationDetector::OrientationDetector( const GenericVector* allowed_scripts, OSResults* osr) { osr_ = osr; allowed_scripts_ = allowed_scripts; } // Score the given blob and return true if it is now sure of the orientation // after adding this block. bool OrientationDetector::detect_blob(BLOB_CHOICE_LIST* scores) { float blob_o_score[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float total_blob_o_score = 0.0f; for (int i = 0; i < 4; ++i) { BLOB_CHOICE_IT choice_it(scores + i); if (!choice_it.empty()) { BLOB_CHOICE* choice = NULL; if (allowed_scripts_ != NULL && !allowed_scripts_->empty()) { // Find the top choice in an allowed script. for (choice_it.mark_cycle_pt(); !choice_it.cycled_list() && choice == NULL; choice_it.forward()) { int choice_script = choice_it.data()->script_id(); int s = 0; for (s = 0; s < allowed_scripts_->size(); ++s) { if ((*allowed_scripts_)[s] == choice_script) { choice = choice_it.data(); break; } } } } else { choice = choice_it.data(); } if (choice != NULL) { // The certainty score ranges between [-20,0]. This is converted here to // [0,1], with 1 indicating best match. blob_o_score[i] = 1 + 0.05 * choice->certainty(); total_blob_o_score += blob_o_score[i]; } } } if (total_blob_o_score == 0.0) return false; // Fill in any blanks with the worst score of the others. This is better than // picking an arbitrary probability for it and way better than -inf. float worst_score = 0.0f; int num_good_scores = 0; for (int i = 0; i < 4; ++i) { if (blob_o_score[i] > 0.0f) { ++num_good_scores; if (worst_score == 0.0f || blob_o_score[i] < worst_score) worst_score = blob_o_score[i]; } } if (num_good_scores == 1) { // Lower worst if there is only one. worst_score /= 2.0f; } for (int i = 0; i < 4; ++i) { if (blob_o_score[i] == 0.0f) { blob_o_score[i] = worst_score; total_blob_o_score += worst_score; } } // Normalize the orientation scores for the blob and use them to // update the aggregated orientation score. for (int i = 0; total_blob_o_score != 0 && i < 4; ++i) { osr_->orientations[i] += log(blob_o_score[i] / total_blob_o_score); } // TODO(ranjith) Add an early exit test, based on min_orientation_margin, // as used in pagesegmain.cpp. return false; } int OrientationDetector::get_orientation() { osr_->update_best_orientation(); return osr_->best_result.orientation_id; } ScriptDetector::ScriptDetector(const GenericVector* allowed_scripts, OSResults* osr, tesseract::Tesseract* tess) { osr_ = osr; tess_ = tess; allowed_scripts_ = allowed_scripts; katakana_id_ = tess_->unicharset.add_script(katakana_script); hiragana_id_ = tess_->unicharset.add_script(hiragana_script); han_id_ = tess_->unicharset.add_script(han_script); hangul_id_ = tess_->unicharset.add_script(hangul_script); japanese_id_ = tess_->unicharset.add_script(japanese_script_); korean_id_ = tess_->unicharset.add_script(korean_script_); latin_id_ = tess_->unicharset.add_script(latin_script); fraktur_id_ = tess_->unicharset.add_script(fraktur_script_); } // Score the given blob and return true if it is now sure of the script after // adding this blob. void ScriptDetector::detect_blob(BLOB_CHOICE_LIST* scores) { bool done[kMaxNumberOfScripts]; for (int i = 0; i < 4; ++i) { for (int j = 0; j < kMaxNumberOfScripts; ++j) done[j] = false; BLOB_CHOICE_IT choice_it; choice_it.set_to_list(scores + i); float prev_score = -1; int script_count = 0; int prev_id = -1; int prev_fontinfo_id = -1; const char* prev_unichar = ""; const char* unichar = ""; for (choice_it.mark_cycle_pt(); !choice_it.cycled_list(); choice_it.forward()) { BLOB_CHOICE* choice = choice_it.data(); int id = choice->script_id(); if (allowed_scripts_ != NULL && !allowed_scripts_->empty()) { // Check that the choice is in an allowed script. int s = 0; for (s = 0; s < allowed_scripts_->size(); ++s) { if ((*allowed_scripts_)[s] == id) break; } if (s == allowed_scripts_->size()) continue; // Not found in list. } // Script already processed before. if (done[id]) continue; done[id] = true; unichar = tess_->unicharset.id_to_unichar(choice->unichar_id()); // Save data from the first match if (prev_score < 0) { prev_score = -choice->certainty(); script_count = 1; prev_id = id; prev_unichar = unichar; prev_fontinfo_id = choice->fontinfo_id(); } else if (-choice->certainty() < prev_score + kNonAmbiguousMargin) { ++script_count; } if (strlen(prev_unichar) == 1) if (unichar[0] >= '0' && unichar[0] <= '9') break; // if script_count is >= 2, character is ambiguous, skip other matches // since they are useless. if (script_count >= 2) break; } // Character is non ambiguous if (script_count == 1) { // Update the score of the winning script osr_->scripts_na[i][prev_id] += 1.0; // Workaround for Fraktur if (prev_id == latin_id_) { if (prev_fontinfo_id >= 0) { const tesseract::FontInfo &fi = tess_->get_fontinfo_table().get(prev_fontinfo_id); //printf("Font: %s i:%i b:%i f:%i s:%i k:%i (%s)\n", fi.name, // fi.is_italic(), fi.is_bold(), fi.is_fixed_pitch(), // fi.is_serif(), fi.is_fraktur(), // prev_unichar); if (fi.is_fraktur()) { osr_->scripts_na[i][prev_id] -= 1.0; osr_->scripts_na[i][fraktur_id_] += 1.0; } } } // Update Japanese / Korean pseudo-scripts if (prev_id == katakana_id_) osr_->scripts_na[i][japanese_id_] += 1.0; if (prev_id == hiragana_id_) osr_->scripts_na[i][japanese_id_] += 1.0; if (prev_id == hangul_id_) osr_->scripts_na[i][korean_id_] += 1.0; if (prev_id == han_id_) { osr_->scripts_na[i][korean_id_] += kHanRatioInKorean; osr_->scripts_na[i][japanese_id_] += kHanRatioInJapanese; } } } // iterate over each orientation } bool ScriptDetector::must_stop(int orientation) { osr_->update_best_script(orientation); return osr_->best_result.sconfidence > 1; } // Helper method to convert an orientation index to its value in degrees. // The value represents the amount of clockwise rotation in degrees that must be // applied for the text to be upright (readable). int OrientationIdToValue(const int& id) { switch (id) { case 0: return 0; case 1: return 270; case 2: return 180; case 3: return 90; default: return -1; } } tesseract-3.04.01/ccmain/osdetect.h000066400000000000000000000110401266071204500170700ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: osdetect.h // Description: Orientation and script detection. // Author: Samuel Charron // Ranjith Unnikrishnan // // (C) Copyright 2008, 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_CCMAIN_OSDETECT_H__ #define TESSERACT_CCMAIN_OSDETECT_H__ #include "strngs.h" #include "unicharset.h" class TO_BLOCK_LIST; class BLOBNBOX; class BLOB_CHOICE_LIST; class BLOBNBOX_CLIST; namespace tesseract { class Tesseract; } // Max number of scripts in ICU + "NULL" + Japanese and Korean + Fraktur const int kMaxNumberOfScripts = 116 + 1 + 2 + 1; struct OSBestResult { OSBestResult() : orientation_id(0), script_id(0), sconfidence(0.0), oconfidence(0.0) {} int orientation_id; int script_id; float sconfidence; float oconfidence; }; struct OSResults { OSResults() : unicharset(NULL) { for (int i = 0; i < 4; ++i) { for (int j = 0; j < kMaxNumberOfScripts; ++j) scripts_na[i][j] = 0; orientations[i] = 0; } } void update_best_orientation(); // Set the estimate of the orientation to the given id. void set_best_orientation(int orientation_id); // Update/Compute the best estimate of the script assuming the given // orientation id. void update_best_script(int orientation_id); // Return the index of the script with the highest score for this orientation. TESS_API int get_best_script(int orientation_id) const; // Accumulate scores with given OSResults instance and update the best script. void accumulate(const OSResults& osr); // Print statistics. void print_scores(void) const; void print_scores(int orientation_id) const; // Array holding scores for each orientation id [0,3]. // Orientation ids [0..3] map to [0, 270, 180, 90] degree orientations of the // page respectively, where the values refer to the amount of clockwise // rotation to be applied to the page for the text to be upright and readable. float orientations[4]; // Script confidence scores for each of 4 possible orientations. float scripts_na[4][kMaxNumberOfScripts]; UNICHARSET* unicharset; OSBestResult best_result; }; class OrientationDetector { public: OrientationDetector(const GenericVector* allowed_scripts, OSResults* results); bool detect_blob(BLOB_CHOICE_LIST* scores); int get_orientation(); private: OSResults* osr_; const GenericVector* allowed_scripts_; }; class ScriptDetector { public: ScriptDetector(const GenericVector* allowed_scripts, OSResults* osr, tesseract::Tesseract* tess); void detect_blob(BLOB_CHOICE_LIST* scores); bool must_stop(int orientation); private: OSResults* osr_; static const char* korean_script_; static const char* japanese_script_; static const char* fraktur_script_; int korean_id_; int japanese_id_; int katakana_id_; int hiragana_id_; int han_id_; int hangul_id_; int latin_id_; int fraktur_id_; tesseract::Tesseract* tess_; const GenericVector* allowed_scripts_; }; int orientation_and_script_detection(STRING& filename, OSResults*, tesseract::Tesseract*); int os_detect(TO_BLOCK_LIST* port_blocks, OSResults* osr, tesseract::Tesseract* tess); int os_detect_blobs(const GenericVector* allowed_scripts, BLOBNBOX_CLIST* blob_list, OSResults* osr, tesseract::Tesseract* tess); bool os_detect_blob(BLOBNBOX* bbox, OrientationDetector* o, ScriptDetector* s, OSResults*, tesseract::Tesseract* tess); // Helper method to convert an orientation index to its value in degrees. // The value represents the amount of clockwise rotation in degrees that must be // applied for the text to be upright (readable). TESS_API int OrientationIdToValue(const int& id); #endif // TESSERACT_CCMAIN_OSDETECT_H__ tesseract-3.04.01/ccmain/output.cpp000066400000000000000000000373721266071204500171710ustar00rootroot00000000000000/****************************************************************** * File: output.cpp (Formerly output.c) * Description: Output pass * Author: Phil Cheatle * Created: Thu Aug 4 10:56:08 BST 1994 * * (C) Copyright 1994, 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. * **********************************************************************/ #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #endif #include #include #ifdef __UNIX__ #include #include #include #endif #include "helpers.h" #include "tessvars.h" #include "control.h" #include "reject.h" #include "docqual.h" #include "output.h" #include "globals.h" #include "tesseractclass.h" #define EPAPER_EXT ".ep" #define PAGE_YSIZE 3508 #define CTRL_INSET '\024' //dc4=text inset #define CTRL_FONT '\016' //so=font change #define CTRL_DEFAULT '\017' //si=default font #define CTRL_SHIFT '\022' //dc2=x shift #define CTRL_TAB '\011' //tab #define CTRL_NEWLINE '\012' //newline #define CTRL_HARDLINE '\015' //cr /********************************************************************** * pixels_to_pts * * Convert an integer number of pixels to the nearest integer * number of points. **********************************************************************/ inT32 pixels_to_pts( //convert coords inT32 pixels, inT32 pix_res //resolution ) { float pts; //converted value pts = pixels * 72.0 / pix_res; return (inT32) (pts + 0.5); //round it } namespace tesseract { void Tesseract::output_pass( //Tess output pass //send to api PAGE_RES_IT &page_res_it, const TBOX *target_word_box) { BLOCK_RES *block_of_last_word; BOOL8 force_eol; //During output BLOCK *nextblock; //block of next word WERD *nextword; //next word page_res_it.restart_page (); block_of_last_word = NULL; while (page_res_it.word () != NULL) { check_debug_pt (page_res_it.word (), 120); if (target_word_box) { TBOX current_word_box=page_res_it.word ()->word->bounding_box(); FCOORD center_pt((current_word_box.right()+current_word_box.left())/2,(current_word_box.bottom()+current_word_box.top())/2); if (!target_word_box->contains(center_pt)) { page_res_it.forward (); continue; } } if (tessedit_write_block_separators && block_of_last_word != page_res_it.block ()) { block_of_last_word = page_res_it.block (); } force_eol = (tessedit_write_block_separators && (page_res_it.block () != page_res_it.next_block ())) || (page_res_it.next_word () == NULL); if (page_res_it.next_word () != NULL) nextword = page_res_it.next_word ()->word; else nextword = NULL; if (page_res_it.next_block () != NULL) nextblock = page_res_it.next_block ()->block; else nextblock = NULL; //regardless of tilde crunching write_results(page_res_it, determine_newline_type(page_res_it.word()->word, page_res_it.block()->block, nextword, nextblock), force_eol); page_res_it.forward(); } } /************************************************************************* * write_results() * * All recognition and rejection has now been done. Generate the following: * .txt file - giving the final best choices with NO highlighting * .raw file - giving the tesseract top choice output for each word * .map file - showing how the .txt file has been rejected in the .ep file * epchoice list - a list of one element per word, containing the text for the * epaper. Reject strings are inserted. * inset list - a list of bounding boxes of reject insets - indexed by the * reject strings in the epchoice text. *************************************************************************/ void Tesseract::write_results(PAGE_RES_IT &page_res_it, char newline_type, // type of newline BOOL8 force_eol) { // override tilde crunch? WERD_RES *word = page_res_it.word(); const UNICHARSET &uchset = *word->uch_set; int i; BOOL8 need_reject = FALSE; UNICHAR_ID space = uchset.unichar_to_id(" "); if ((word->unlv_crunch_mode != CR_NONE || word->best_choice->length() == 0) && !tessedit_zero_kelvin_rejection && !tessedit_word_for_word) { if ((word->unlv_crunch_mode != CR_DELETE) && (!stats_.tilde_crunch_written || ((word->unlv_crunch_mode == CR_KEEP_SPACE) && (word->word->space () > 0) && !word->word->flag (W_FUZZY_NON) && !word->word->flag (W_FUZZY_SP)))) { if (!word->word->flag (W_BOL) && (word->word->space () > 0) && !word->word->flag (W_FUZZY_NON) && !word->word->flag (W_FUZZY_SP)) { stats_.last_char_was_tilde = false; } need_reject = TRUE; } if ((need_reject && !stats_.last_char_was_tilde) || (force_eol && stats_.write_results_empty_block)) { /* Write a reject char - mark as rejected unless zero_rejection mode */ stats_.last_char_was_tilde = TRUE; stats_.tilde_crunch_written = true; stats_.last_char_was_newline = false; stats_.write_results_empty_block = false; } if ((word->word->flag (W_EOL) && !stats_.last_char_was_newline) || force_eol) { stats_.tilde_crunch_written = false; stats_.last_char_was_newline = true; stats_.last_char_was_tilde = false; } if (force_eol) stats_.write_results_empty_block = true; return; } /* NORMAL PROCESSING of non tilde crunched words */ stats_.tilde_crunch_written = false; if (newline_type) stats_.last_char_was_newline = true; else stats_.last_char_was_newline = false; stats_.write_results_empty_block = force_eol; // about to write a real word if (unlv_tilde_crunching && stats_.last_char_was_tilde && (word->word->space() == 0) && !(word->word->flag(W_REP_CHAR) && tessedit_write_rep_codes) && (word->best_choice->unichar_id(0) == space)) { /* Prevent adjacent tilde across words - we know that adjacent tildes within words have been removed */ word->MergeAdjacentBlobs(0); } if (newline_type || (word->word->flag (W_REP_CHAR) && tessedit_write_rep_codes)) stats_.last_char_was_tilde = false; else { if (word->reject_map.length () > 0) { if (word->best_choice->unichar_id(word->reject_map.length() - 1) == space) stats_.last_char_was_tilde = true; else stats_.last_char_was_tilde = false; } else if (word->word->space () > 0) stats_.last_char_was_tilde = false; /* else it is unchanged as there are no output chars */ } ASSERT_HOST (word->best_choice->length() == word->reject_map.length()); set_unlv_suspects(word); check_debug_pt (word, 120); if (tessedit_rejection_debug) { tprintf ("Dict word: \"%s\": %d\n", word->best_choice->debug_string().string(), dict_word(*(word->best_choice))); } if (!word->word->flag(W_REP_CHAR) || !tessedit_write_rep_codes) { if (tessedit_zero_rejection) { /* OVERRIDE ALL REJECTION MECHANISMS - ONLY REJECT TESS FAILURES */ for (i = 0; i < word->best_choice->length(); ++i) { if (word->reject_map[i].rejected()) word->reject_map[i].setrej_minimal_rej_accept(); } } if (tessedit_minimal_rejection) { /* OVERRIDE ALL REJECTION MECHANISMS - ONLY REJECT TESS FAILURES */ for (i = 0; i < word->best_choice->length(); ++i) { if ((word->best_choice->unichar_id(i) != space) && word->reject_map[i].rejected()) word->reject_map[i].setrej_minimal_rej_accept(); } } } } } // namespace tesseract /********************************************************************** * determine_newline_type * * Find whether we have a wrapping or hard newline. * Return FALSE if not at end of line. **********************************************************************/ char determine_newline_type( //test line ends WERD *word, //word to do BLOCK *block, //current block WERD *next_word, //next word BLOCK *next_block //block of next word ) { inT16 end_gap; //to right edge inT16 width; //of next word TBOX word_box; //bounding TBOX next_box; //next word TBOX block_box; //block bounding if (!word->flag (W_EOL)) return FALSE; //not end of line if (next_word == NULL || next_block == NULL || block != next_block) return CTRL_NEWLINE; if (next_word->space () > 0) return CTRL_HARDLINE; //it is tabbed word_box = word->bounding_box (); next_box = next_word->bounding_box (); block_box = block->bounding_box (); //gap to eol end_gap = block_box.right () - word_box.right (); end_gap -= (inT32) block->space (); width = next_box.right () - next_box.left (); // tprintf("end_gap=%d-%d=%d, width=%d-%d=%d, nl=%d\n", // block_box.right(),word_box.right(),end_gap, // next_box.right(),next_box.left(),width, // end_gap>width ? CTRL_HARDLINE : CTRL_NEWLINE); return end_gap > width ? CTRL_HARDLINE : CTRL_NEWLINE; } /************************************************************************* * get_rep_char() * Return the first accepted character from the repetition string. This is the * character which is repeated - as determined earlier by fix_rep_char() *************************************************************************/ namespace tesseract { UNICHAR_ID Tesseract::get_rep_char(WERD_RES *word) { // what char is repeated? int i; for (i = 0; ((i < word->reject_map.length()) && (word->reject_map[i].rejected())); ++i); if (i < word->reject_map.length()) { return word->best_choice->unichar_id(i); } else { return word->uch_set->unichar_to_id(unrecognised_char.string()); } } /************************************************************************* * SUSPECT LEVELS * * 0 - don't reject ANYTHING * 1,2 - partial rejection * 3 - BEST * * NOTE: to reject JUST tess failures in the .map file set suspect_level 3 and * tessedit_minimal_rejection. *************************************************************************/ void Tesseract::set_unlv_suspects(WERD_RES *word_res) { int len = word_res->reject_map.length(); const WERD_CHOICE &word = *(word_res->best_choice); const UNICHARSET &uchset = *word.unicharset(); int i; float rating_per_ch; if (suspect_level == 0) { for (i = 0; i < len; i++) { if (word_res->reject_map[i].rejected()) word_res->reject_map[i].setrej_minimal_rej_accept(); } return; } if (suspect_level >= 3) return; //Use defaults /* NOW FOR LEVELS 1 and 2 Find some stuff to unreject*/ if (safe_dict_word(word_res) && (count_alphas(word) > suspect_short_words)) { /* Unreject alphas in dictionary words */ for (i = 0; i < len; ++i) { if (word_res->reject_map[i].rejected() && uchset.get_isalpha(word.unichar_id(i))) word_res->reject_map[i].setrej_minimal_rej_accept(); } } rating_per_ch = word.rating() / word_res->reject_map.length(); if (rating_per_ch >= suspect_rating_per_ch) return; //Don't touch bad ratings if ((word_res->tess_accepted) || (rating_per_ch < suspect_accept_rating)) { /* Unreject any Tess Acceptable word - but NOT tess reject chs*/ for (i = 0; i < len; ++i) { if (word_res->reject_map[i].rejected() && (!uchset.eq(word.unichar_id(i), " "))) word_res->reject_map[i].setrej_minimal_rej_accept(); } } for (i = 0; i < len; i++) { if (word_res->reject_map[i].rejected()) { if (word_res->reject_map[i].flag(R_DOC_REJ)) word_res->reject_map[i].setrej_minimal_rej_accept(); if (word_res->reject_map[i].flag(R_BLOCK_REJ)) word_res->reject_map[i].setrej_minimal_rej_accept(); if (word_res->reject_map[i].flag(R_ROW_REJ)) word_res->reject_map[i].setrej_minimal_rej_accept(); } } if (suspect_level == 2) return; if (!suspect_constrain_1Il || (word_res->reject_map.length() <= suspect_short_words)) { for (i = 0; i < len; i++) { if (word_res->reject_map[i].rejected()) { if ((word_res->reject_map[i].flag(R_1IL_CONFLICT) || word_res->reject_map[i].flag(R_POSTNN_1IL))) word_res->reject_map[i].setrej_minimal_rej_accept(); if (!suspect_constrain_1Il && word_res->reject_map[i].flag(R_MM_REJECT)) word_res->reject_map[i].setrej_minimal_rej_accept(); } } } if (acceptable_word_string(*word_res->uch_set, word.unichar_string().string(), word.unichar_lengths().string()) != AC_UNACCEPTABLE || acceptable_number_string(word.unichar_string().string(), word.unichar_lengths().string())) { if (word_res->reject_map.length() > suspect_short_words) { for (i = 0; i < len; i++) { if (word_res->reject_map[i].rejected() && (!word_res->reject_map[i].perm_rejected() || word_res->reject_map[i].flag (R_1IL_CONFLICT) || word_res->reject_map[i].flag (R_POSTNN_1IL) || word_res->reject_map[i].flag (R_MM_REJECT))) { word_res->reject_map[i].setrej_minimal_rej_accept(); } } } } } inT16 Tesseract::count_alphas(const WERD_CHOICE &word) { int count = 0; for (int i = 0; i < word.length(); ++i) { if (word.unicharset()->get_isalpha(word.unichar_id(i))) count++; } return count; } inT16 Tesseract::count_alphanums(const WERD_CHOICE &word) { int count = 0; for (int i = 0; i < word.length(); ++i) { if (word.unicharset()->get_isalpha(word.unichar_id(i)) || word.unicharset()->get_isdigit(word.unichar_id(i))) count++; } return count; } BOOL8 Tesseract::acceptable_number_string(const char *s, const char *lengths) { BOOL8 prev_digit = FALSE; if (*lengths == 1 && *s == '(') s++; if (*lengths == 1 && ((*s == '$') || (*s == '.') || (*s == '+') || (*s == '-'))) s++; for (; *s != '\0'; s += *(lengths++)) { if (unicharset.get_isdigit(s, *lengths)) prev_digit = TRUE; else if (prev_digit && (*lengths == 1 && ((*s == '.') || (*s == ',') || (*s == '-')))) prev_digit = FALSE; else if (prev_digit && *lengths == 1 && (*(s + *lengths) == '\0') && ((*s == '%') || (*s == ')'))) return TRUE; else if (prev_digit && *lengths == 1 && (*s == '%') && (*(lengths + 1) == 1 && *(s + *lengths) == ')') && (*(s + *lengths + *(lengths + 1)) == '\0')) return TRUE; else return FALSE; } return TRUE; } } // namespace tesseract tesseract-3.04.01/ccmain/output.h000066400000000000000000000025501266071204500166240ustar00rootroot00000000000000/****************************************************************** * File: output.h (Formerly output.h) * Description: Output pass * Author: Phil Cheatle * Created: Thu Aug 4 10:56:08 BST 1994 * * (C) Copyright 1994, 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 OUTPUT_H #define OUTPUT_H #include "params.h" //#include "epapconv.h" #include "pageres.h" /** test line ends */ char determine_newline_type(WERD *word, ///< word to do BLOCK *block, ///< current block WERD *next_word, ///< next word BLOCK *next_block ///< block of next word ); #endif tesseract-3.04.01/ccmain/pageiterator.cpp000066400000000000000000000557661266071204500203260ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: pageiterator.cpp // Description: Iterator for tesseract page structure that avoids using // tesseract internal data structures. // Author: Ray Smith // Created: Fri Feb 26 14:32:09 PST 2010 // // (C) Copyright 2010, 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 "pageiterator.h" #include "allheaders.h" #include "helpers.h" #include "pageres.h" #include "tesseractclass.h" namespace tesseract { PageIterator::PageIterator(PAGE_RES* page_res, Tesseract* tesseract, int scale, int scaled_yres, int rect_left, int rect_top, int rect_width, int rect_height) : page_res_(page_res), tesseract_(tesseract), word_(NULL), word_length_(0), blob_index_(0), cblob_it_(NULL), include_upper_dots_(false), include_lower_dots_(false), scale_(scale), scaled_yres_(scaled_yres), rect_left_(rect_left), rect_top_(rect_top), rect_width_(rect_width), rect_height_(rect_height) { it_ = new PAGE_RES_IT(page_res); PageIterator::Begin(); } PageIterator::~PageIterator() { delete it_; delete cblob_it_; } /** * PageIterators may be copied! This makes it possible to iterate over * all the objects at a lower level, while maintaining an iterator to * objects at a higher level. */ PageIterator::PageIterator(const PageIterator& src) : page_res_(src.page_res_), tesseract_(src.tesseract_), word_(NULL), word_length_(src.word_length_), blob_index_(src.blob_index_), cblob_it_(NULL), include_upper_dots_(src.include_upper_dots_), include_lower_dots_(src.include_lower_dots_), scale_(src.scale_), scaled_yres_(src.scaled_yres_), rect_left_(src.rect_left_), rect_top_(src.rect_top_), rect_width_(src.rect_width_), rect_height_(src.rect_height_) { it_ = new PAGE_RES_IT(*src.it_); BeginWord(src.blob_index_); } const PageIterator& PageIterator::operator=(const PageIterator& src) { page_res_ = src.page_res_; tesseract_ = src.tesseract_; include_upper_dots_ = src.include_upper_dots_; include_lower_dots_ = src.include_lower_dots_; scale_ = src.scale_; scaled_yres_ = src.scaled_yres_; rect_left_ = src.rect_left_; rect_top_ = src.rect_top_; rect_width_ = src.rect_width_; rect_height_ = src.rect_height_; if (it_ != NULL) delete it_; it_ = new PAGE_RES_IT(*src.it_); BeginWord(src.blob_index_); return *this; } bool PageIterator::PositionedAtSameWord(const PAGE_RES_IT* other) const { return (it_ == NULL && it_ == other) || ((other != NULL) && (it_ != NULL) && (*it_ == *other)); } // ============= Moving around within the page ============. /** Resets the iterator to point to the start of the page. */ void PageIterator::Begin() { it_->restart_page_with_empties(); BeginWord(0); } void PageIterator::RestartParagraph() { if (it_->block() == NULL) return; // At end of the document. PAGE_RES_IT para(page_res_); PAGE_RES_IT next_para(para); next_para.forward_paragraph(); while (next_para.cmp(*it_) <= 0) { para = next_para; next_para.forward_paragraph(); } *it_ = para; BeginWord(0); } bool PageIterator::IsWithinFirstTextlineOfParagraph() const { PageIterator p_start(*this); p_start.RestartParagraph(); return p_start.it_->row() == it_->row(); } void PageIterator::RestartRow() { it_->restart_row(); BeginWord(0); } /** * Moves to the start of the next object at the given level in the * page hierarchy, and returns false if the end of the page was reached. * NOTE (CHANGED!) that ALL PageIteratorLevel level values will visit each * non-text block at least once. * Think of non text blocks as containing a single para, with at least one * line, with a single imaginary word, containing a single symbol. * The bounding boxes mark out any polygonal nature of the block, and * PTIsTextType(BLockType()) is false for non-text blocks. * Calls to Next with different levels may be freely intermixed. * This function iterates words in right-to-left scripts correctly, if * the appropriate language has been loaded into Tesseract. */ bool PageIterator::Next(PageIteratorLevel level) { if (it_->block() == NULL) return false; // Already at the end! if (it_->word() == NULL) level = RIL_BLOCK; switch (level) { case RIL_BLOCK: it_->forward_block(); break; case RIL_PARA: it_->forward_paragraph(); break; case RIL_TEXTLINE: for (it_->forward_with_empties(); it_->row() == it_->prev_row(); it_->forward_with_empties()); break; case RIL_WORD: it_->forward_with_empties(); break; case RIL_SYMBOL: if (cblob_it_ != NULL) cblob_it_->forward(); ++blob_index_; if (blob_index_ >= word_length_) it_->forward_with_empties(); else return true; break; } BeginWord(0); return it_->block() != NULL; } /** * Returns true if the iterator is at the start of an object at the given * level. Possible uses include determining if a call to Next(RIL_WORD) * moved to the start of a RIL_PARA. */ bool PageIterator::IsAtBeginningOf(PageIteratorLevel level) const { if (it_->block() == NULL) return false; // Already at the end! if (it_->word() == NULL) return true; // In an image block. switch (level) { case RIL_BLOCK: return blob_index_ == 0 && it_->block() != it_->prev_block(); case RIL_PARA: return blob_index_ == 0 && (it_->block() != it_->prev_block() || it_->row()->row->para() != it_->prev_row()->row->para()); case RIL_TEXTLINE: return blob_index_ == 0 && it_->row() != it_->prev_row(); case RIL_WORD: return blob_index_ == 0; case RIL_SYMBOL: return true; } return false; } /** * Returns whether the iterator is positioned at the last element in a * given level. (e.g. the last word in a line, the last line in a block) */ bool PageIterator::IsAtFinalElement(PageIteratorLevel level, PageIteratorLevel element) const { if (Empty(element)) return true; // Already at the end! // The result is true if we step forward by element and find we are // at the the end of the page or at beginning of *all* levels in: // [level, element). // When there is more than one level difference between element and level, // we could for instance move forward one symbol and still be at the first // word on a line, so we also have to be at the first symbol in a word. PageIterator next(*this); next.Next(element); if (next.Empty(element)) return true; // Reached the end of the page. while (element > level) { element = static_cast(element - 1); if (!next.IsAtBeginningOf(element)) return false; } return true; } /** * Returns whether this iterator is positioned * before other: -1 * equal to other: 0 * after other: 1 */ int PageIterator::Cmp(const PageIterator &other) const { int word_cmp = it_->cmp(*other.it_); if (word_cmp != 0) return word_cmp; if (blob_index_ < other.blob_index_) return -1; if (blob_index_ == other.blob_index_) return 0; return 1; } // ============= Accessing data ==============. // Coordinate system: // Integer coordinates are at the cracks between the pixels. // The top-left corner of the top-left pixel in the image is at (0,0). // The bottom-right corner of the bottom-right pixel in the image is at // (width, height). // Every bounding box goes from the top-left of the top-left contained // pixel to the bottom-right of the bottom-right contained pixel, so // the bounding box of the single top-left pixel in the image is: // (0,0)->(1,1). // If an image rectangle has been set in the API, then returned coordinates // relate to the original (full) image, rather than the rectangle. /** * Returns the bounding rectangle of the current object at the given level in * the coordinates of the working image that is pix_binary(). * See comment on coordinate system above. * Returns false if there is no such object at the current position. */ bool PageIterator::BoundingBoxInternal(PageIteratorLevel level, int* left, int* top, int* right, int* bottom) const { if (Empty(level)) return false; TBOX box; PARA *para = NULL; switch (level) { case RIL_BLOCK: box = it_->block()->block->restricted_bounding_box(include_upper_dots_, include_lower_dots_); break; case RIL_PARA: para = it_->row()->row->para(); // explicit fall-through. case RIL_TEXTLINE: box = it_->row()->row->restricted_bounding_box(include_upper_dots_, include_lower_dots_); break; case RIL_WORD: box = it_->word()->word->restricted_bounding_box(include_upper_dots_, include_lower_dots_); break; case RIL_SYMBOL: if (cblob_it_ == NULL) box = it_->word()->box_word->BlobBox(blob_index_); else box = cblob_it_->data()->bounding_box(); } if (level == RIL_PARA) { PageIterator other = *this; other.Begin(); do { if (other.it_->block() && other.it_->block()->block == it_->block()->block && other.it_->row() && other.it_->row()->row && other.it_->row()->row->para() == para) { box = box.bounding_union(other.it_->row()->row->bounding_box()); } } while (other.Next(RIL_TEXTLINE)); } if (level != RIL_SYMBOL || cblob_it_ != NULL) box.rotate(it_->block()->block->re_rotation()); // Now we have a box in tesseract coordinates relative to the image rectangle, // we have to convert the coords to a top-down system. const int pix_height = pixGetHeight(tesseract_->pix_binary()); const int pix_width = pixGetWidth(tesseract_->pix_binary()); *left = ClipToRange(static_cast(box.left()), 0, pix_width); *top = ClipToRange(pix_height - box.top(), 0, pix_height); *right = ClipToRange(static_cast(box.right()), *left, pix_width); *bottom = ClipToRange(pix_height - box.bottom(), *top, pix_height); return true; } /** * Returns the bounding rectangle of the current object at the given level in * coordinates of the original image. * See comment on coordinate system above. * Returns false if there is no such object at the current position. */ 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_ - padding, rect_left_, rect_left_ + rect_width_); *top = ClipToRange(*top / scale_ + rect_top_ - padding, rect_top_, rect_top_ + rect_height_); *right = ClipToRange((*right + scale_ - 1) / scale_ + rect_left_ + padding, *left, rect_left_ + rect_width_); *bottom = ClipToRange((*bottom + scale_ - 1) / scale_ + rect_top_ + padding, *top, rect_top_ + rect_height_); return true; } /** Return that there is no such object at a given level. */ bool PageIterator::Empty(PageIteratorLevel level) const { if (it_->block() == NULL) return true; // Already at the end! if (it_->word() == NULL && level != RIL_BLOCK) return true; // image block if (level == RIL_SYMBOL && blob_index_ >= word_length_) return true; // Zero length word, or already at the end of it. return false; } /** Returns the type of the current block. See apitypes.h for PolyBlockType. */ PolyBlockType PageIterator::BlockType() const { if (it_->block() == NULL || it_->block()->block == NULL) return PT_UNKNOWN; // Already at the end! if (it_->block()->block->poly_block() == NULL) return PT_FLOWING_TEXT; // No layout analysis used - assume text. return it_->block()->block->poly_block()->isA(); } /** Returns the polygon outline of the current block. The returned Pta must * be ptaDestroy-ed after use. */ Pta* PageIterator::BlockPolygon() const { if (it_->block() == NULL || it_->block()->block == NULL) return NULL; // Already at the end! if (it_->block()->block->poly_block() == NULL) return NULL; // No layout analysis used - no polygon. ICOORDELT_IT it(it_->block()->block->poly_block()->points()); Pta* pta = ptaCreate(it.length()); int num_pts = 0; for (it.mark_cycle_pt(); !it.cycled_list(); it.forward(), ++num_pts) { ICOORD* pt = it.data(); // Convert to top-down coords within the input image. float x = static_cast(pt->x()) / scale_ + rect_left_; float y = rect_top_ + rect_height_ - static_cast(pt->y()) / scale_; ptaAddPt(pta, x, y); } return pta; } /** * Returns a binary image of the current object at the given level. * The position and size match the return from BoundingBoxInternal, and so this * could be upscaled with respect to the original input image. * Use pixDestroy to delete the image after use. * The following methods are used to generate the images: * RIL_BLOCK: mask the page image with the block polygon. * RIL_TEXTLINE: Clip the rectangle of the line box from the page image. * TODO(rays) fix this to generate and use a line polygon. * RIL_WORD: Clip the rectangle of the word box from the page image. * RIL_SYMBOL: Render the symbol outline to an image for cblobs (prior * to recognition) or the bounding box otherwise. * A reconstruction of the original image (using xor to check for double * representation) should be reasonably accurate, * apart from removed noise, at the block level. Below the block level, the * reconstruction will be missing images and line separators. * At the symbol level, kerned characters will be invade the bounding box * if rendered after recognition, making an xor reconstruction inaccurate, but * an or construction better. Before recognition, symbol-level reconstruction * should be good, even with xor, since the images come from the connected * components. */ Pix* PageIterator::GetBinaryImage(PageIteratorLevel level) const { int left, top, right, bottom; if (!BoundingBoxInternal(level, &left, &top, &right, &bottom)) return NULL; if (level == RIL_SYMBOL && cblob_it_ != NULL && cblob_it_->data()->area() != 0) return cblob_it_->data()->render(); Box* box = boxCreate(left, top, right - left, bottom - top); Pix* pix = pixClipRectangle(tesseract_->pix_binary(), box, NULL); boxDestroy(&box); if (level == RIL_BLOCK || level == RIL_PARA) { // Clip to the block polygon as well. TBOX mask_box; Pix* mask = it_->block()->block->render_mask(&mask_box); int mask_x = left - mask_box.left(); int mask_y = top - (tesseract_->ImageHeight() - mask_box.top()); // AND the mask and pix, putting the result in pix. pixRasterop(pix, MAX(0, -mask_x), MAX(0, -mask_y), pixGetWidth(pix), pixGetHeight(pix), PIX_SRC & PIX_DST, mask, MAX(0, mask_x), MAX(0, mask_y)); pixDestroy(&mask); } return pix; } /** * Returns an image of the current object at the given level in greyscale * if available in the input. To guarantee a binary image use BinaryImage. * NOTE that in order to give the best possible image, the bounds are * expanded slightly over the binary connected component, by the supplied * padding, so the top-left position of the returned image is returned * in (left,top). These will most likely not match the coordinates * returned by BoundingBox. * If you do not supply an original image, you will get a binary one. * Use pixDestroy to delete the image after use. */ Pix* PageIterator::GetImage(PageIteratorLevel level, int padding, Pix* original_img, int* left, int* top) const { int right, bottom; if (!BoundingBox(level, left, top, &right, &bottom)) return NULL; if (original_img == NULL) return GetBinaryImage(level); // Expand the box. *left = MAX(*left - padding, 0); *top = MAX(*top - padding, 0); right = MIN(right + padding, rect_width_); bottom = MIN(bottom + padding, rect_height_); Box* box = boxCreate(*left, *top, right - *left, bottom - *top); Pix* grey_pix = pixClipRectangle(original_img, box, NULL); boxDestroy(&box); if (level == RIL_BLOCK || level == RIL_PARA) { // Clip to the block polygon as well. TBOX mask_box; Pix* mask = it_->block()->block->render_mask(&mask_box); // Copy the mask registered correctly into an image the size of grey_pix. int mask_x = *left - mask_box.left(); int mask_y = *top - (pixGetHeight(original_img) - mask_box.top()); int width = pixGetWidth(grey_pix); int height = pixGetHeight(grey_pix); Pix* resized_mask = pixCreate(width, height, 1); pixRasterop(resized_mask, MAX(0, -mask_x), MAX(0, -mask_y), width, height, PIX_SRC, mask, MAX(0, mask_x), MAX(0, mask_y)); pixDestroy(&mask); pixDilateBrick(resized_mask, resized_mask, 2 * padding + 1, 2 * padding + 1); pixInvert(resized_mask, resized_mask); pixSetMasked(grey_pix, resized_mask, MAX_UINT32); pixDestroy(&resized_mask); } return grey_pix; } /** * Returns the baseline of the current object at the given level. * The baseline is the line that passes through (x1, y1) and (x2, y2). * WARNING: with vertical text, baselines may be vertical! */ bool PageIterator::Baseline(PageIteratorLevel level, int* x1, int* y1, int* x2, int* y2) const { if (it_->word() == NULL) return false; // Already at the end! ROW* row = it_->row()->row; WERD* word = it_->word()->word; TBOX box = (level == RIL_WORD || level == RIL_SYMBOL) ? word->bounding_box() : row->bounding_box(); int left = box.left(); ICOORD startpt(left, static_cast(row->base_line(left) + 0.5)); int right = box.right(); ICOORD endpt(right, static_cast(row->base_line(right) + 0.5)); // Rotate to image coordinates and convert to global image coords. startpt.rotate(it_->block()->block->re_rotation()); endpt.rotate(it_->block()->block->re_rotation()); *x1 = startpt.x() / scale_ + rect_left_; *y1 = (rect_height_ - startpt.y()) / scale_ + rect_top_; *x2 = endpt.x() / scale_ + rect_left_; *y2 = (rect_height_ - endpt.y()) / scale_ + rect_top_; return true; } void PageIterator::Orientation(tesseract::Orientation *orientation, tesseract::WritingDirection *writing_direction, tesseract::TextlineOrder *textline_order, float *deskew_angle) const { BLOCK* block = it_->block()->block; // Orientation FCOORD up_in_image(0.0, 1.0); up_in_image.unrotate(block->classify_rotation()); up_in_image.rotate(block->re_rotation()); if (up_in_image.x() == 0.0F) { if (up_in_image.y() > 0.0F) { *orientation = ORIENTATION_PAGE_UP; } else { *orientation = ORIENTATION_PAGE_DOWN; } } else if (up_in_image.x() > 0.0F) { *orientation = ORIENTATION_PAGE_RIGHT; } else { *orientation = ORIENTATION_PAGE_LEFT; } // Writing direction bool is_vertical_text = (block->classify_rotation().x() == 0.0); bool right_to_left = block->right_to_left(); *writing_direction = is_vertical_text ? WRITING_DIRECTION_TOP_TO_BOTTOM : (right_to_left ? WRITING_DIRECTION_RIGHT_TO_LEFT : WRITING_DIRECTION_LEFT_TO_RIGHT); // Textline Order bool is_mongolian = false; // TODO(eger): fix me *textline_order = is_vertical_text ? (is_mongolian ? TEXTLINE_ORDER_LEFT_TO_RIGHT : TEXTLINE_ORDER_RIGHT_TO_LEFT) : TEXTLINE_ORDER_TOP_TO_BOTTOM; // Deskew angle FCOORD skew = block->skew(); // true horizontal for textlines *deskew_angle = -skew.angle(); } void PageIterator::ParagraphInfo(tesseract::ParagraphJustification *just, bool *is_list_item, bool *is_crown, int *first_line_indent) const { *just = tesseract::JUSTIFICATION_UNKNOWN; if (!it_->row() || !it_->row()->row || !it_->row()->row->para() || !it_->row()->row->para()->model) return; PARA *para = it_->row()->row->para(); *is_list_item = para->is_list_item; *is_crown = para->is_very_first_or_continuation; *first_line_indent = para->model->first_indent() - para->model->body_indent(); } /** * Sets up the internal data for iterating the blobs of a new word, then * moves the iterator to the given offset. */ void PageIterator::BeginWord(int offset) { WERD_RES* word_res = it_->word(); if (word_res == NULL) { // This is a non-text block, so there is no word. word_length_ = 0; blob_index_ = 0; word_ = NULL; return; } if (word_res->best_choice != NULL) { // Recognition has been done, so we are using the box_word, which // is already baseline denormalized. word_length_ = word_res->best_choice->length(); 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_); } word_ = NULL; // We will be iterating the box_word. if (cblob_it_ != NULL) { delete cblob_it_; cblob_it_ = NULL; } } else { // No recognition yet, so a "symbol" is a cblob. word_ = word_res->word; ASSERT_HOST(word_->cblob_list() != NULL); word_length_ = word_->cblob_list()->length(); if (cblob_it_ == NULL) cblob_it_ = new C_BLOB_IT; cblob_it_->set_to_list(word_->cblob_list()); } for (blob_index_ = 0; blob_index_ < offset; ++blob_index_) { if (cblob_it_ != NULL) cblob_it_->forward(); } } bool PageIterator::SetWordBlamerBundle(BlamerBundle *blamer_bundle) { if (it_->word() != NULL) { it_->word()->blamer_bundle = blamer_bundle; return true; } else { return false; } } } // namespace tesseract. tesseract-3.04.01/ccmain/pageiterator.h000066400000000000000000000355121266071204500177560ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: pageiterator.h // Description: Iterator for tesseract page structure that avoids using // tesseract internal data structures. // Author: Ray Smith // Created: Fri Feb 26 11:01:06 PST 2010 // // (C) Copyright 2010, 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_CCMAIN_PAGEITERATOR_H__ #define TESSERACT_CCMAIN_PAGEITERATOR_H__ #include "publictypes.h" #include "platform.h" struct BlamerBundle; class C_BLOB_IT; class PAGE_RES; class PAGE_RES_IT; class WERD; struct Pix; struct Pta; namespace tesseract { class Tesseract; /** * Class to iterate over tesseract page structure, providing access to all * levels of the page hierarchy, without including any tesseract headers or * having to handle any tesseract structures. * WARNING! This class points to data held within the TessBaseAPI class, and * therefore can only be used while the TessBaseAPI class still exists and * has not been subjected to a call of Init, SetImage, Recognize, Clear, End * DetectOS, or anything else that changes the internal PAGE_RES. * See apitypes.h for the definition of PageIteratorLevel. * See also ResultIterator, derived from PageIterator, which adds in the * ability to access OCR output with text-specific methods. */ class TESS_API PageIterator { public: /** * page_res and tesseract come directly from the BaseAPI. * The rectangle parameters are copied indirectly from the Thresholder, * via the BaseAPI. They represent the coordinates of some rectangle in an * original image (in top-left-origin coordinates) and therefore the top-left * needs to be added to any output boxes in order to specify coordinates * in the original image. See TessBaseAPI::SetRectangle. * The scale and scaled_yres are in case the Thresholder scaled the image * rectangle prior to thresholding. Any coordinates in tesseract's image * must be divided by scale before adding (rect_left, rect_top). * The scaled_yres indicates the effective resolution of the binary image * that tesseract has been given by the Thresholder. * After the constructor, Begin has already been called. */ PageIterator(PAGE_RES* page_res, Tesseract* tesseract, int scale, int scaled_yres, int rect_left, int rect_top, int rect_width, int rect_height); virtual ~PageIterator(); /** * Page/ResultIterators may be copied! This makes it possible to iterate over * all the objects at a lower level, while maintaining an iterator to * objects at a higher level. These constructors DO NOT CALL Begin, so * iterations will continue from the location of src. */ PageIterator(const PageIterator& src); const PageIterator& operator=(const PageIterator& src); /** Are we positioned at the same location as other? */ bool PositionedAtSameWord(const PAGE_RES_IT* other) const; // ============= Moving around within the page ============. /** * Moves the iterator to point to the start of the page to begin an * iteration. */ virtual void Begin(); /** * Moves the iterator to the beginning of the paragraph. * This class implements this functionality by moving it to the zero indexed * blob of the first (leftmost) word on the first row of the paragraph. */ virtual void RestartParagraph(); /** * Return whether this iterator points anywhere in the first textline of a * paragraph. */ bool IsWithinFirstTextlineOfParagraph() const; /** * Moves the iterator to the beginning of the text line. * This class implements this functionality by moving it to the zero indexed * blob of the first (leftmost) word of the row. */ virtual void RestartRow(); /** * Moves to the start of the next object at the given level in the * page hierarchy, and returns false if the end of the page was reached. * NOTE that RIL_SYMBOL will skip non-text blocks, but all other * PageIteratorLevel level values will visit each non-text block once. * Think of non text blocks as containing a single para, with a single line, * with a single imaginary word. * Calls to Next with different levels may be freely intermixed. * This function iterates words in right-to-left scripts correctly, if * the appropriate language has been loaded into Tesseract. */ virtual bool Next(PageIteratorLevel level); /** * Returns true if the iterator is at the start of an object at the given * level. * * For instance, suppose an iterator it is pointed to the first symbol of the * first word of the third line of the second paragraph of the first block in * a page, then: * it.IsAtBeginningOf(RIL_BLOCK) = false * it.IsAtBeginningOf(RIL_PARA) = false * it.IsAtBeginningOf(RIL_TEXTLINE) = true * it.IsAtBeginningOf(RIL_WORD) = true * it.IsAtBeginningOf(RIL_SYMBOL) = true */ virtual bool IsAtBeginningOf(PageIteratorLevel level) const; /** * Returns whether the iterator is positioned at the last element in a * given level. (e.g. the last word in a line, the last line in a block) * * Here's some two-paragraph example * text. It starts off innocuously * enough but quickly turns bizarre. * The author inserts a cornucopia * of words to guard against confused * references. * * Now take an iterator it pointed to the start of "bizarre." * it.IsAtFinalElement(RIL_PARA, RIL_SYMBOL) = false * it.IsAtFinalElement(RIL_PARA, RIL_WORD) = true * it.IsAtFinalElement(RIL_BLOCK, RIL_WORD) = false */ virtual bool IsAtFinalElement(PageIteratorLevel level, PageIteratorLevel element) const; /** * Returns whether this iterator is positioned * before other: -1 * equal to other: 0 * after other: 1 */ int Cmp(const PageIterator &other) const; // ============= Accessing data ==============. // Coordinate system: // Integer coordinates are at the cracks between the pixels. // The top-left corner of the top-left pixel in the image is at (0,0). // The bottom-right corner of the bottom-right pixel in the image is at // (width, height). // Every bounding box goes from the top-left of the top-left contained // pixel to the bottom-right of the bottom-right contained pixel, so // the bounding box of the single top-left pixel in the image is: // (0,0)->(1,1). // If an image rectangle has been set in the API, then returned coordinates // relate to the original (full) image, rather than the rectangle. /** * Controls what to include in a bounding box. Bounding boxes of all levels * between RIL_WORD and RIL_BLOCK can include or exclude potential diacritics. * Between layout analysis and recognition, it isn't known where all * diacritics belong, so this control is used to include or exclude some * diacritics that are above or below the main body of the word. In most cases * where the placement is obvious, and after recognition, it doesn't make as * much difference, as the diacritics will already be included in the word. */ void SetBoundingBoxComponents(bool include_upper_dots, bool include_lower_dots) { include_upper_dots_ = include_upper_dots; include_lower_dots_ = include_lower_dots; } /** * Returns the bounding rectangle of the current object at the given level. * See comment on coordinate system above. * Returns false if there is no such object at the current position. * The returned bounding box is guaranteed to match the size and position * of the image returned by GetBinaryImage, but may clip foreground pixels * from a grey image. The padding argument to GetImage can be used to expand * the image to include more foreground pixels. See GetImage below. */ 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 * respect to the original image and is scaled by a factor scale_. */ bool BoundingBoxInternal(PageIteratorLevel level, int* left, int* top, int* right, int* bottom) const; /** Returns whether there is no object of a given level. */ bool Empty(PageIteratorLevel level) const; /** * Returns the type of the current block. See apitypes.h for * PolyBlockType. */ PolyBlockType BlockType() const; /** * Returns the polygon outline of the current block. The returned Pta must * be ptaDestroy-ed after use. Note that the returned Pta lists the vertices * of the polygon, and the last edge is the line segment between the last * point and the first point. NULL will be returned if the iterator is * at the end of the document or layout analysis was not used. */ Pta* BlockPolygon() const; /** * Returns a binary image of the current object at the given level. * The position and size match the return from BoundingBoxInternal, and so * this could be upscaled with respect to the original input image. * Use pixDestroy to delete the image after use. */ Pix* GetBinaryImage(PageIteratorLevel level) const; /** * Returns an image of the current object at the given level in greyscale * if available in the input. To guarantee a binary image use BinaryImage. * NOTE that in order to give the best possible image, the bounds are * expanded slightly over the binary connected component, by the supplied * padding, so the top-left position of the returned image is returned * in (left,top). These will most likely not match the coordinates * returned by BoundingBox. * If you do not supply an original image, you will get a binary one. * Use pixDestroy to delete the image after use. */ Pix* GetImage(PageIteratorLevel level, int padding, Pix* original_img, int* left, int* top) const; /** * Returns the baseline of the current object at the given level. * The baseline is the line that passes through (x1, y1) and (x2, y2). * WARNING: with vertical text, baselines may be vertical! * Returns false if there is no baseline at the current position. */ bool Baseline(PageIteratorLevel level, int* x1, int* y1, int* x2, int* y2) const; /** * Returns orientation for the block the iterator points to. * orientation, writing_direction, textline_order: see publictypes.h * deskew_angle: after rotating the block so the text orientation is * upright, how many radians does one have to rotate the * block anti-clockwise for it to be level? * -Pi/4 <= deskew_angle <= Pi/4 */ void Orientation(tesseract::Orientation *orientation, tesseract::WritingDirection *writing_direction, tesseract::TextlineOrder *textline_order, float *deskew_angle) const; /** * Returns information about the current paragraph, if available. * * justification - * LEFT if ragged right, or fully justified and script is left-to-right. * RIGHT if ragged left, or fully justified and script is right-to-left. * unknown if it looks like source code or we have very few lines. * is_list_item - * true if we believe this is a member of an ordered or unordered list. * is_crown - * true if the first line of the paragraph is aligned with the other * lines of the paragraph even though subsequent paragraphs have first * line indents. This typically indicates that this is the continuation * of a previous paragraph or that it is the very first paragraph in * the chapter. * first_line_indent - * For LEFT aligned paragraphs, the first text line of paragraphs of * this kind are indented this many pixels from the left edge of the * rest of the paragraph. * for RIGHT aligned paragraphs, the first text line of paragraphs of * this kind are indented this many pixels from the right edge of the * rest of the paragraph. * NOTE 1: This value may be negative. * NOTE 2: if *is_crown == true, the first line of this paragraph is * actually flush, and first_line_indent is set to the "common" * first_line_indent for subsequent paragraphs in this block * of text. */ void ParagraphInfo(tesseract::ParagraphJustification *justification, bool *is_list_item, 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 * moves the iterator to the given offset. */ TESS_LOCAL void BeginWord(int offset); /** Pointer to the page_res owned by the API. */ PAGE_RES* page_res_; /** Pointer to the Tesseract object owned by the API. */ Tesseract* tesseract_; /** * The iterator to the page_res_. Owned by this ResultIterator. * A pointer just to avoid dragging in Tesseract includes. */ PAGE_RES_IT* it_; /** * The current input WERD being iterated. If there is an output from OCR, * then word_ is NULL. Owned by the API */ WERD* word_; /** The length of the current word_. */ int word_length_; /** The current blob index within the word. */ int blob_index_; /** * Iterator to the blobs within the word. If NULL, then we are iterating * OCR results in the box_word. * Owned by this ResultIterator. */ C_BLOB_IT* cblob_it_; /** Control over what to include in bounding boxes. */ bool include_upper_dots_; bool include_lower_dots_; /** Parameters saved from the Thresholder. Needed to rebuild coordinates.*/ int scale_; int scaled_yres_; int rect_left_; int rect_top_; int rect_width_; int rect_height_; }; } // namespace tesseract. #endif // TESSERACT_CCMAIN_PAGEITERATOR_H__ tesseract-3.04.01/ccmain/pagesegmain.cpp000066400000000000000000000412051266071204500200770ustar00rootroot00000000000000/********************************************************************** * File: pagesegmain.cpp * Description: Top-level page segmenter for Tesseract. * Author: Ray Smith * Created: Thu Sep 25 17:12:01 PDT 2008 * * (C) Copyright 2008, 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. * **********************************************************************/ #ifdef _WIN32 #ifndef __GNUC__ #include #endif // __GNUC__ #ifndef unlink #include #endif #else #include #endif // _WIN32 #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #endif // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "allheaders.h" #include "blobbox.h" #include "blread.h" #include "colfind.h" #include "equationdetect.h" #include "imagefind.h" #include "linefind.h" #include "makerow.h" #include "osdetect.h" #include "tabvector.h" #include "tesseractclass.h" #include "tessvars.h" #include "textord.h" #include "tordmain.h" #include "wordseg.h" namespace tesseract { /// Minimum believable resolution. const int kMinCredibleResolution = 70; /// Default resolution used if input in not believable. const int kDefaultResolution = 300; // Max erosions to perform in removing an enclosing circle. const int kMaxCircleErosions = 8; // Helper to remove an enclosing circle from an image. // If there isn't one, then the image will most likely get badly mangled. // The returned pix must be pixDestroyed after use. NULL may be returned // if the image doesn't meet the trivial conditions that it uses to determine // success. static Pix* RemoveEnclosingCircle(Pix* pixs) { Pix* pixsi = pixInvert(NULL, pixs); Pix* pixc = pixCreateTemplate(pixs); pixSetOrClearBorder(pixc, 1, 1, 1, 1, PIX_SET); pixSeedfillBinary(pixc, pixc, pixsi, 4); pixInvert(pixc, pixc); pixDestroy(&pixsi); Pix* pixt = pixAnd(NULL, pixs, pixc); l_int32 max_count; pixCountConnComp(pixt, 8, &max_count); // The count has to go up before we start looking for the minimum. l_int32 min_count = MAX_INT32; Pix* pixout = NULL; for (int i = 1; i < kMaxCircleErosions; i++) { pixDestroy(&pixt); pixErodeBrick(pixc, pixc, 3, 3); pixt = pixAnd(NULL, pixs, pixc); l_int32 count; pixCountConnComp(pixt, 8, &count); if (i == 1 || count > max_count) { max_count = count; min_count = count; } else if (i > 1 && count < min_count) { min_count = count; pixDestroy(&pixout); pixout = pixCopy(NULL, pixt); // Save the best. } else if (count >= min_count) { break; // We have passed by the best. } } pixDestroy(&pixt); pixDestroy(&pixc); return pixout; } /** * Segment the page according to the current value of tessedit_pageseg_mode. * pix_binary_ is used as the source image and should not be NULL. * On return the blocks list owns all the constructed page layout. */ int Tesseract::SegmentPage(const STRING* input_file, BLOCK_LIST* blocks, Tesseract* osd_tess, OSResults* osr) { ASSERT_HOST(pix_binary_ != NULL); int width = pixGetWidth(pix_binary_); int height = pixGetHeight(pix_binary_); // Get page segmentation mode. PageSegMode pageseg_mode = static_cast( static_cast(tessedit_pageseg_mode)); // If a UNLV zone file can be found, use that instead of segmentation. if (!PSM_COL_FIND_ENABLED(pageseg_mode) && input_file != NULL && input_file->length() > 0) { STRING name = *input_file; const char* lastdot = strrchr(name.string(), '.'); if (lastdot != NULL) name[lastdot - name.string()] = '\0'; read_unlv_file(name, width, height, blocks); } if (blocks->empty()) { // No UNLV file present. Work according to the PageSegMode. // First make a single block covering the whole image. BLOCK_IT block_it(blocks); BLOCK* block = new BLOCK("", TRUE, 0, 0, 0, 0, width, height); block->set_right_to_left(right_to_left()); block_it.add_to_end(block); } else { // UNLV file present. Use PSM_SINGLE_BLOCK. pageseg_mode = PSM_SINGLE_BLOCK; } // The diacritic_blobs holds noise blobs that may be diacritics. They // are separated out on areas of the image that seem noisy and short-circuit // the layout process, going straight from the initial partition creation // right through to after word segmentation, where they are added to the // rej_cblobs list of the most appropriate word. From there classification // will determine whether they are used. BLOBNBOX_LIST diacritic_blobs; int auto_page_seg_ret_val = 0; TO_BLOCK_LIST to_blocks; if (PSM_OSD_ENABLED(pageseg_mode) || PSM_BLOCK_FIND_ENABLED(pageseg_mode) || PSM_SPARSE(pageseg_mode)) { auto_page_seg_ret_val = AutoPageSeg( pageseg_mode, blocks, &to_blocks, enable_noise_removal ? &diacritic_blobs : NULL, osd_tess, osr); if (pageseg_mode == PSM_OSD_ONLY) return auto_page_seg_ret_val; // To create blobs from the image region bounds uncomment this line: // to_blocks.clear(); // Uncomment to go back to the old mode. } else { deskew_ = FCOORD(1.0f, 0.0f); reskew_ = FCOORD(1.0f, 0.0f); if (pageseg_mode == PSM_CIRCLE_WORD) { Pix* pixcleaned = RemoveEnclosingCircle(pix_binary_); if (pixcleaned != NULL) { pixDestroy(&pix_binary_); pix_binary_ = pixcleaned; } } } if (auto_page_seg_ret_val < 0) { return -1; } if (blocks->empty()) { if (textord_debug_tabfind) tprintf("Empty page\n"); return 0; // AutoPageSeg found an empty page. } bool splitting = pageseg_devanagari_split_strategy != ShiroRekhaSplitter::NO_SPLIT; bool cjk_mode = textord_use_cjk_fp_model; textord_.TextordPage(pageseg_mode, reskew_, width, height, pix_binary_, pix_thresholds_, pix_grey_, splitting || cjk_mode, &diacritic_blobs, blocks, &to_blocks); return auto_page_seg_ret_val; } // Helper writes a grey image to a file for use by scrollviewer. // Normally for speed we don't display the image in the layout debug windows. // If textord_debug_images is true, we draw the image as a background to some // of the debug windows. printable determines whether these // images are optimized for printing instead of screen display. static void WriteDebugBackgroundImage(bool printable, Pix* pix_binary) { Pix* grey_pix = pixCreate(pixGetWidth(pix_binary), pixGetHeight(pix_binary), 8); // Printable images are light grey on white, but for screen display // they are black on dark grey so the other colors show up well. if (printable) { pixSetAll(grey_pix); pixSetMasked(grey_pix, pix_binary, 192); } else { pixSetAllArbitrary(grey_pix, 64); pixSetMasked(grey_pix, pix_binary, 0); } AlignedBlob::IncrementDebugPix(); pixWrite(AlignedBlob::textord_debug_pix().string(), grey_pix, IFF_PNG); pixDestroy(&grey_pix); } /** * Auto page segmentation. Divide the page image into blocks of uniform * text linespacing and images. * * Resolution (in ppi) is derived from the input image. * * The output goes in the blocks list with corresponding TO_BLOCKs in the * to_blocks list. * * If !PSM_COL_FIND_ENABLED(pageseg_mode), then no attempt is made to divide * the image into columns, but multiple blocks are still made if the text is * of non-uniform linespacing. * * If diacritic_blobs is non-null, then diacritics/noise blobs, that would * confuse layout anaylsis by causing textline overlap, are placed there, * with the expectation that they will be reassigned to words later and * noise/diacriticness determined via classification. * * If osd (orientation and script detection) is true then that is performed * as well. If only_osd is true, then only orientation and script detection is * performed. If osd is desired, (osd or only_osd) then osr_tess must be * another Tesseract that was initialized especially for osd, and the results * will be output into osr (orientation and script result). */ int Tesseract::AutoPageSeg(PageSegMode pageseg_mode, BLOCK_LIST* blocks, TO_BLOCK_LIST* to_blocks, BLOBNBOX_LIST* diacritic_blobs, Tesseract* osd_tess, OSResults* osr) { if (textord_debug_images) { WriteDebugBackgroundImage(textord_debug_printable, pix_binary_); } Pix* photomask_pix = NULL; Pix* musicmask_pix = NULL; // The blocks made by the ColumnFinder. Moved to blocks before return. BLOCK_LIST found_blocks; TO_BLOCK_LIST temp_blocks; ColumnFinder* finder = SetupPageSegAndDetectOrientation( pageseg_mode, blocks, osd_tess, osr, &temp_blocks, &photomask_pix, &musicmask_pix); int result = 0; if (finder != NULL) { TO_BLOCK_IT to_block_it(&temp_blocks); TO_BLOCK* to_block = to_block_it.data(); if (musicmask_pix != NULL) { // TODO(rays) pass the musicmask_pix into FindBlocks and mark music // blocks separately. For now combine with photomask_pix. pixOr(photomask_pix, photomask_pix, musicmask_pix); } if (equ_detect_) { finder->SetEquationDetect(equ_detect_); } result = finder->FindBlocks( pageseg_mode, scaled_color_, scaled_factor_, to_block, photomask_pix, pix_thresholds_, pix_grey_, &found_blocks, diacritic_blobs, to_blocks); if (result >= 0) finder->GetDeskewVectors(&deskew_, &reskew_); delete finder; } pixDestroy(&photomask_pix); pixDestroy(&musicmask_pix); if (result < 0) return result; blocks->clear(); BLOCK_IT block_it(blocks); // Move the found blocks to the input/output blocks. block_it.add_list_after(&found_blocks); if (textord_debug_images) { // The debug image is no longer needed so delete it. unlink(AlignedBlob::textord_debug_pix().string()); } return result; } // Helper adds all the scripts from sid_set converted to ids from osd_set to // allowed_ids. static void AddAllScriptsConverted(const UNICHARSET& sid_set, const UNICHARSET& osd_set, GenericVector* allowed_ids) { for (int i = 0; i < sid_set.get_script_table_size(); ++i) { if (i != sid_set.null_sid()) { const char* script = sid_set.get_script_from_script_id(i); allowed_ids->push_back(osd_set.get_script_id_from_name(script)); } } } /** * Sets up auto page segmentation, determines the orientation, and corrects it. * Somewhat arbitrary chunk of functionality, factored out of AutoPageSeg to * facilitate testing. * photo_mask_pix is a pointer to a NULL pointer that will be filled on return * with the leptonica photo mask, which must be pixDestroyed by the caller. * to_blocks is an empty list that will be filled with (usually a single) * block that is used during layout analysis. This ugly API is required * because of the possibility of a unlv zone file. * TODO(rays) clean this up. * See AutoPageSeg for other arguments. * The returned ColumnFinder must be deleted after use. */ ColumnFinder* Tesseract::SetupPageSegAndDetectOrientation( PageSegMode pageseg_mode, BLOCK_LIST* blocks, Tesseract* osd_tess, OSResults* osr, TO_BLOCK_LIST* to_blocks, Pix** photo_mask_pix, Pix** music_mask_pix) { int vertical_x = 0; int vertical_y = 1; TabVector_LIST v_lines; TabVector_LIST h_lines; ICOORD bleft(0, 0); ASSERT_HOST(pix_binary_ != NULL); if (tessedit_dump_pageseg_images) { pixWrite("tessinput.png", pix_binary_, IFF_PNG); } // Leptonica is used to find the rule/separator lines in the input. LineFinder::FindAndRemoveLines(source_resolution_, textord_tabfind_show_vlines, pix_binary_, &vertical_x, &vertical_y, music_mask_pix, &v_lines, &h_lines); if (tessedit_dump_pageseg_images) pixWrite("tessnolines.png", pix_binary_, IFF_PNG); // Leptonica is used to find a mask of the photo regions in the input. *photo_mask_pix = ImageFind::FindImages(pix_binary_); if (tessedit_dump_pageseg_images) pixWrite("tessnoimages.png", pix_binary_, IFF_PNG); if (!PSM_COL_FIND_ENABLED(pageseg_mode)) v_lines.clear(); // The rest of the algorithm uses the usual connected components. textord_.find_components(pix_binary_, blocks, to_blocks); TO_BLOCK_IT to_block_it(to_blocks); // There must be exactly one input block. // TODO(rays) handle new textline finding with a UNLV zone file. ASSERT_HOST(to_blocks->singleton()); TO_BLOCK* to_block = to_block_it.data(); TBOX blkbox = to_block->block->bounding_box(); ColumnFinder* finder = NULL; if (to_block->line_size >= 2) { finder = new ColumnFinder(static_cast(to_block->line_size), blkbox.botleft(), blkbox.topright(), source_resolution_, textord_use_cjk_fp_model, textord_tabfind_aligned_gap_fraction, &v_lines, &h_lines, vertical_x, vertical_y); finder->SetupAndFilterNoise(pageseg_mode, *photo_mask_pix, to_block); if (equ_detect_) { equ_detect_->LabelSpecialText(to_block); } BLOBNBOX_CLIST osd_blobs; // osd_orientation is the number of 90 degree rotations to make the // characters upright. (See osdetect.h for precise definition.) // We want the text lines horizontal, (vertical text indicates vertical // textlines) which may conflict (eg vertically written CJK). int osd_orientation = 0; bool vertical_text = textord_tabfind_force_vertical_text || pageseg_mode == PSM_SINGLE_BLOCK_VERT_TEXT; if (!vertical_text && textord_tabfind_vertical_text && PSM_ORIENTATION_ENABLED(pageseg_mode)) { vertical_text = finder->IsVerticallyAlignedText(textord_tabfind_vertical_text_ratio, to_block, &osd_blobs); } if (PSM_OSD_ENABLED(pageseg_mode) && osd_tess != NULL && osr != NULL) { GenericVector osd_scripts; if (osd_tess != this) { // We are running osd as part of layout analysis, so constrain the // scripts to those allowed by *this. AddAllScriptsConverted(unicharset, osd_tess->unicharset, &osd_scripts); for (int s = 0; s < sub_langs_.size(); ++s) { AddAllScriptsConverted(sub_langs_[s]->unicharset, osd_tess->unicharset, &osd_scripts); } } os_detect_blobs(&osd_scripts, &osd_blobs, osr, osd_tess); if (pageseg_mode == PSM_OSD_ONLY) { delete finder; return NULL; } osd_orientation = osr->best_result.orientation_id; double osd_score = osr->orientations[osd_orientation]; double osd_margin = min_orientation_margin * 2; for (int i = 0; i < 4; ++i) { if (i != osd_orientation && osd_score - osr->orientations[i] < osd_margin) { osd_margin = osd_score - osr->orientations[i]; } } int best_script_id = osr->best_result.script_id; const char* best_script_str = osd_tess->unicharset.get_script_from_script_id(best_script_id); bool cjk = best_script_id == osd_tess->unicharset.han_sid() || best_script_id == osd_tess->unicharset.hiragana_sid() || best_script_id == osd_tess->unicharset.katakana_sid() || strcmp("Japanese", best_script_str) == 0 || strcmp("Korean", best_script_str) == 0 || strcmp("Hangul", best_script_str) == 0; if (cjk) { finder->set_cjk_script(true); } if (osd_margin < min_orientation_margin) { // The margin is weak. if (!cjk && !vertical_text && osd_orientation == 2) { // upside down latin text is improbable with such a weak margin. tprintf("OSD: Weak margin (%.2f), horiz textlines, not CJK: " "Don't rotate.\n", osd_margin); osd_orientation = 0; } else { tprintf("OSD: Weak margin (%.2f) for %d blob text block, " "but using orientation anyway: %d\n", osd_blobs.length(), osd_margin, osd_orientation); } } } osd_blobs.shallow_clear(); finder->CorrectOrientation(to_block, vertical_text, osd_orientation); } return finder; } } // namespace tesseract. tesseract-3.04.01/ccmain/pagewalk.cpp000066400000000000000000000031521266071204500174110ustar00rootroot00000000000000/********************************************************************** * File: pagewalk.cpp (Formerly walkers.c) * Description: Block list processors * Author: Phil Cheatle * Created: Thu Oct 10 16:25:24 BST 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include "pageres.h" #include "tesseractclass.h" namespace tesseract { /** * @name process_selected_words() * * Walk the current block list applying the specified word processor function * to each word that overlaps the selection_box. */ void Tesseract::process_selected_words( PAGE_RES* page_res, // blocks to check TBOX & selection_box, BOOL8(tesseract::Tesseract::*word_processor)(PAGE_RES_IT* pr_it)) { for (PAGE_RES_IT page_res_it(page_res); page_res_it.word() != NULL; page_res_it.forward()) { WERD* word = page_res_it.word()->word; if (word->bounding_box().overlap(selection_box)) { if (!(this->*word_processor)(&page_res_it)) return; } } } } // namespace tesseract tesseract-3.04.01/ccmain/par_control.cpp000066400000000000000000000045651266071204500201510ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: par_control.cpp // Description: Control code for parallel implementation. // Author: Ray Smith // Created: Mon Nov 04 13:23:15 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 "tesseractclass.h" #ifdef OPENMP #include #endif // OPENMP namespace tesseract { struct BlobData { BlobData() : blob(NULL), choices(NULL) {} BlobData(int index, Tesseract* tess, const WERD_RES& word) : blob(word.chopped_word->blobs[index]), tesseract(tess), choices(&(*word.ratings)(index, index)) {} TBLOB* blob; Tesseract* tesseract; BLOB_CHOICE_LIST** choices; }; void Tesseract::PrerecAllWordsPar(const GenericVector& words) { // Prepare all the blobs. GenericVector blobs; for (int w = 0; w < words.size(); ++w) { if (words[w].word->ratings != NULL && words[w].word->ratings->get(0, 0) == NULL) { for (int s = 0; s < words[w].lang_words.size(); ++s) { Tesseract* sub = s < sub_langs_.size() ? sub_langs_[s] : this; const WERD_RES& word = *words[w].lang_words[s]; for (int b = 0; b < word.chopped_word->NumBlobs(); ++b) { blobs.push_back(BlobData(b, sub, word)); } } } } // Pre-classify all the blobs. if (tessedit_parallelize > 1) { #pragma omp parallel for num_threads(10) for (int b = 0; b < blobs.size(); ++b) { *blobs[b].choices = blobs[b].tesseract->classify_blob(blobs[b].blob, "par", White, NULL); } } else { // TODO(AMD) parallelize this. for (int b = 0; b < blobs.size(); ++b) { *blobs[b].choices = blobs[b].tesseract->classify_blob(blobs[b].blob, "par", White, NULL); } } } } // namespace tesseract. tesseract-3.04.01/ccmain/paragraphs.cpp000066400000000000000000002712051266071204500177540ustar00rootroot00000000000000/********************************************************************** * File: paragraphs.cpp * Description: Paragraph detection for tesseract. * Author: David Eger * Created: 25 February 2011 * * (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. * **********************************************************************/ #ifdef _MSC_VER #define __func__ __FUNCTION__ #endif #include #include "genericvector.h" #include "helpers.h" #include "mutableiterator.h" #include "ocrpara.h" #include "pageres.h" #include "paragraphs.h" #include "paragraphs_internal.h" #include "publictypes.h" #include "ratngs.h" #include "rect.h" #include "statistc.h" #include "strngs.h" #include "tprintf.h" #include "unicharset.h" #include "unicodes.h" namespace tesseract { // Special "weak" ParagraphModels. const ParagraphModel *kCrownLeft = reinterpret_cast(0xDEAD111F); const ParagraphModel *kCrownRight = reinterpret_cast(0xDEAD888F); // Given the width of a typical space between words, what is the threshold // by which by which we think left and right alignments for paragraphs // can vary and still be aligned. static int Epsilon(int space_pix) { return space_pix * 4 / 5; } static bool AcceptableRowArgs( int debug_level, int min_num_rows, const char *function_name, const GenericVector *rows, int row_start, int row_end) { if (row_start < 0 || row_end > rows->size() || row_start > row_end) { tprintf("Invalid arguments rows[%d, %d) while rows is of size %d.\n", row_start, row_end, rows->size()); return false; } if (row_end - row_start < min_num_rows) { if (debug_level > 1) { tprintf("# Too few rows[%d, %d) for %s.\n", row_start, row_end, function_name); } return false; } return true; } // =============================== Debug Code ================================ // Convert an integer to a decimal string. static STRING StrOf(int num) { char buffer[30]; snprintf(buffer, sizeof(buffer), "%d", num); return STRING(buffer); } // Given a row-major matrix of unicode text and a column separator, print // a formatted table. For ASCII, we get good column alignment. static void PrintTable(const GenericVector > &rows, const STRING &colsep) { GenericVector max_col_widths; for (int r = 0; r < rows.size(); r++) { int num_columns = rows[r].size(); for (int c = 0; c < num_columns; c++) { int num_unicodes = 0; for (int i = 0; i < rows[r][c].size(); i++) { if ((rows[r][c][i] & 0xC0) != 0x80) num_unicodes++; } if (c >= max_col_widths.size()) { max_col_widths.push_back(num_unicodes); } else { if (num_unicodes > max_col_widths[c]) max_col_widths[c] = num_unicodes; } } } GenericVector col_width_patterns; for (int c = 0; c < max_col_widths.size(); c++) { col_width_patterns.push_back( STRING("%-") + StrOf(max_col_widths[c]) + "s"); } for (int r = 0; r < rows.size(); r++) { for (int c = 0; c < rows[r].size(); c++) { if (c > 0) tprintf("%s", colsep.string()); tprintf(col_width_patterns[c].string(), rows[r][c].string()); } tprintf("\n"); } } STRING RtlEmbed(const STRING &word, bool rtlify) { if (rtlify) return STRING(kRLE) + word + STRING(kPDF); return word; } // Print the current thoughts of the paragraph detector. static void PrintDetectorState(const ParagraphTheory &theory, const GenericVector &rows) { GenericVector > output; output.push_back(GenericVector()); output.back().push_back("#row"); output.back().push_back("space"); output.back().push_back(".."); output.back().push_back("lword[widthSEL]"); output.back().push_back("rword[widthSEL]"); RowScratchRegisters::AppendDebugHeaderFields(&output.back()); output.back().push_back("text"); for (int i = 0; i < rows.size(); i++) { output.push_back(GenericVector()); GenericVector &row = output.back(); const RowInfo& ri = *rows[i].ri_; row.push_back(StrOf(i)); row.push_back(StrOf(ri.average_interword_space)); row.push_back(ri.has_leaders ? ".." : " "); row.push_back(RtlEmbed(ri.lword_text, !ri.ltr) + "[" + StrOf(ri.lword_box.width()) + (ri.lword_likely_starts_idea ? "S" : "s") + (ri.lword_likely_ends_idea ? "E" : "e") + (ri.lword_indicates_list_item ? "L" : "l") + "]"); row.push_back(RtlEmbed(ri.rword_text, !ri.ltr) + "[" + StrOf(ri.rword_box.width()) + (ri.rword_likely_starts_idea ? "S" : "s") + (ri.rword_likely_ends_idea ? "E" : "e") + (ri.rword_indicates_list_item ? "L" : "l") + "]"); rows[i].AppendDebugInfo(theory, &row); row.push_back(RtlEmbed(ri.text, !ri.ltr)); } PrintTable(output, " "); tprintf("Active Paragraph Models:\n"); for (int m = 0; m < theory.models().size(); m++) { tprintf(" %d: %s\n", m + 1, theory.models()[m]->ToString().string()); } } static void DebugDump( bool should_print, const STRING &phase, const ParagraphTheory &theory, const GenericVector &rows) { if (!should_print) return; tprintf("# %s\n", phase.string()); PrintDetectorState(theory, rows); } // Print out the text for rows[row_start, row_end) static void PrintRowRange(const GenericVector &rows, int row_start, int row_end) { tprintf("======================================\n"); for (int row = row_start; row < row_end; row++) { tprintf("%s\n", rows[row].ri_->text.string()); } tprintf("======================================\n"); } // ============= Brain Dead Language Model (ASCII Version) =================== bool IsLatinLetter(int ch) { return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); } bool IsDigitLike(int ch) { return ch == 'o' || ch == 'O' || ch == 'l' || ch == 'I'; } bool IsOpeningPunct(int ch) { return strchr("'\"({[", ch) != NULL; } bool IsTerminalPunct(int ch) { return strchr(":'\".?!]})", ch) != NULL; } // Return a pointer after consuming as much text as qualifies as roman numeral. const char *SkipChars(const char *str, const char *toskip) { while (*str != '\0' && strchr(toskip, *str)) { str++; } return str; } const char *SkipChars(const char *str, bool (*skip)(int)) { while (*str != '\0' && skip(*str)) { str++; } return str; } const char *SkipOne(const char *str, const char *toskip) { if (*str != '\0' && strchr(toskip, *str)) return str + 1; return str; } // Return whether it is very likely that this is a numeral marker that could // start a list item. Some examples include: // A I iii. VI (2) 3.5. [C-4] bool LikelyListNumeral(const STRING &word) { const char *kRomans = "ivxlmdIVXLMD"; const char *kDigits = "012345789"; const char *kOpen = "[{("; const char *kSep = ":;-.,"; const char *kClose = "]})"; int num_segments = 0; const char *pos = word.string(); while (*pos != '\0' && num_segments < 3) { // skip up to two open parens. const char *numeral_start = SkipOne(SkipOne(pos, kOpen), kOpen); const char *numeral_end = SkipChars(numeral_start, kRomans); if (numeral_end != numeral_start) { // Got Roman Numeral. Great. } else { numeral_end = SkipChars(numeral_start, kDigits); if (numeral_end == numeral_start) { // If there's a single latin letter, we can use that. numeral_end = SkipChars(numeral_start, IsLatinLetter); if (numeral_end - numeral_start != 1) break; } } // We got some sort of numeral. num_segments++; // Skip any trailing parens or punctuation. pos = SkipChars(SkipChars(numeral_end, kClose), kSep); if (pos == numeral_end) break; } return *pos == '\0'; } bool LikelyListMark(const STRING &word) { const char *kListMarks = "0Oo*.,+."; return word.size() == 1 && strchr(kListMarks, word[0]) != NULL; } bool AsciiLikelyListItem(const STRING &word) { return LikelyListMark(word) || LikelyListNumeral(word); } // ========== Brain Dead Language Model (Tesseract Version) ================ // Return the first Unicode Codepoint from werd[pos]. int UnicodeFor(const UNICHARSET *u, const WERD_CHOICE *werd, int pos) { if (!u || !werd || pos > werd->length()) return 0; return UNICHAR(u->id_to_unichar(werd->unichar_id(pos)), -1).first_uni(); } // A useful helper class for finding the first j >= i so that word[j] // does not have given character type. class UnicodeSpanSkipper { public: UnicodeSpanSkipper(const UNICHARSET *unicharset, const WERD_CHOICE *word) : u_(unicharset), word_(word) { wordlen_ = word->length(); } // Given an input position, return the first position >= pos not punc. int SkipPunc(int pos); // Given an input position, return the first position >= pos not digit. int SkipDigits(int pos); // Given an input position, return the first position >= pos not roman. int SkipRomans(int pos); // Given an input position, return the first position >= pos not alpha. int SkipAlpha(int pos); private: const UNICHARSET *u_; const WERD_CHOICE *word_; int wordlen_; }; int UnicodeSpanSkipper::SkipPunc(int pos) { while (pos < wordlen_ && u_->get_ispunctuation(word_->unichar_id(pos))) pos++; return pos; } int UnicodeSpanSkipper::SkipDigits(int pos) { while (pos < wordlen_ && (u_->get_isdigit(word_->unichar_id(pos)) || IsDigitLike(UnicodeFor(u_, word_, pos)))) pos++; return pos; } int UnicodeSpanSkipper::SkipRomans(int pos) { const char *kRomans = "ivxlmdIVXLMD"; while (pos < wordlen_) { int ch = UnicodeFor(u_, word_, pos); if (ch >= 0xF0 || strchr(kRomans, ch) == 0) break; pos++; } return pos; } int UnicodeSpanSkipper::SkipAlpha(int pos) { while (pos < wordlen_ && u_->get_isalpha(word_->unichar_id(pos))) pos++; return pos; } bool LikelyListMarkUnicode(int ch) { if (ch < 0x80) { STRING single_ch; single_ch += ch; return LikelyListMark(single_ch); } switch (ch) { // TODO(eger) expand this list of unicodes as needed. case 0x00B0: // degree sign case 0x2022: // bullet case 0x25E6: // white bullet case 0x00B7: // middle dot case 0x25A1: // white square case 0x25A0: // black square case 0x25AA: // black small square case 0x2B1D: // black very small square case 0x25BA: // black right-pointing pointer case 0x25CF: // black circle case 0x25CB: // white circle return true; default: break; // fall through } return false; } // Return whether it is very likely that this is a numeral marker that could // start a list item. Some examples include: // A I iii. VI (2) 3.5. [C-4] bool UniLikelyListItem(const UNICHARSET *u, const WERD_CHOICE *werd) { if (werd->length() == 1 && LikelyListMarkUnicode(UnicodeFor(u, werd, 0))) return true; UnicodeSpanSkipper m(u, werd); int num_segments = 0; int pos = 0; while (pos < werd->length() && num_segments < 3) { int numeral_start = m.SkipPunc(pos); if (numeral_start > pos + 1) break; int numeral_end = m.SkipRomans(numeral_start); if (numeral_end == numeral_start) { numeral_end = m.SkipDigits(numeral_start); if (numeral_end == numeral_start) { // If there's a single latin letter, we can use that. numeral_end = m.SkipAlpha(numeral_start); if (numeral_end - numeral_start != 1) break; } } // We got some sort of numeral. num_segments++; // Skip any trailing punctuation. pos = m.SkipPunc(numeral_end); if (pos == numeral_end) break; } return pos == werd->length(); } // ========= Brain Dead Language Model (combined entry points) ================ // Given the leftmost word of a line either as a Tesseract unicharset + werd // or a utf8 string, set the following attributes for it: // is_list - this word might be a list number or bullet. // starts_idea - this word is likely to start a sentence. // ends_idea - this word is likely to end a sentence. void LeftWordAttributes(const UNICHARSET *unicharset, const WERD_CHOICE *werd, const STRING &utf8, bool *is_list, bool *starts_idea, bool *ends_idea) { *is_list = false; *starts_idea = false; *ends_idea = false; if (utf8.size() == 0 || (werd != NULL && werd->length() == 0)) { // Empty *ends_idea = true; return; } if (unicharset && werd) { // We have a proper werd and unicharset so use it. if (UniLikelyListItem(unicharset, werd)) { *is_list = true; *starts_idea = true; *ends_idea = true; } if (unicharset->get_isupper(werd->unichar_id(0))) { *starts_idea = true; } if (unicharset->get_ispunctuation(werd->unichar_id(0))) { *starts_idea = true; *ends_idea = true; } } else { // Assume utf8 is mostly ASCII if (AsciiLikelyListItem(utf8)) { *is_list = true; *starts_idea = true; } int start_letter = utf8[0]; if (IsOpeningPunct(start_letter)) { *starts_idea = true; } if (IsTerminalPunct(start_letter)) { *ends_idea = true; } if (start_letter >= 'A' && start_letter <= 'Z') { *starts_idea = true; } } } // Given the rightmost word of a line either as a Tesseract unicharset + werd // or a utf8 string, set the following attributes for it: // is_list - this word might be a list number or bullet. // starts_idea - this word is likely to start a sentence. // ends_idea - this word is likely to end a sentence. void RightWordAttributes(const UNICHARSET *unicharset, const WERD_CHOICE *werd, const STRING &utf8, bool *is_list, bool *starts_idea, bool *ends_idea) { *is_list = false; *starts_idea = false; *ends_idea = false; if (utf8.size() == 0 || (werd != NULL && werd->length() == 0)) { // Empty *ends_idea = true; return; } if (unicharset && werd) { // We have a proper werd and unicharset so use it. if (UniLikelyListItem(unicharset, werd)) { *is_list = true; *starts_idea = true; } UNICHAR_ID last_letter = werd->unichar_id(werd->length() - 1); if (unicharset->get_ispunctuation(last_letter)) { *ends_idea = true; } } else { // Assume utf8 is mostly ASCII if (AsciiLikelyListItem(utf8)) { *is_list = true; *starts_idea = true; } int last_letter = utf8[utf8.size() - 1]; if (IsOpeningPunct(last_letter) || IsTerminalPunct(last_letter)) { *ends_idea = true; } } } // =============== Implementation of RowScratchRegisters ===================== /* static */ void RowScratchRegisters::AppendDebugHeaderFields( GenericVector *header) { header->push_back("[lmarg,lind;rind,rmarg]"); header->push_back("model"); } void RowScratchRegisters::AppendDebugInfo(const ParagraphTheory &theory, GenericVector *dbg) const { char s[30]; snprintf(s, sizeof(s), "[%3d,%3d;%3d,%3d]", lmargin_, lindent_, rindent_, rmargin_); dbg->push_back(s); STRING model_string; model_string += static_cast(GetLineType()); model_string += ":"; int model_numbers = 0; for (int h = 0; h < hypotheses_.size(); h++) { if (hypotheses_[h].model == NULL) continue; if (model_numbers > 0) model_string += ","; if (StrongModel(hypotheses_[h].model)) { model_string += StrOf(1 + theory.IndexOf(hypotheses_[h].model)); } else if (hypotheses_[h].model == kCrownLeft) { model_string += "CrL"; } else if (hypotheses_[h].model == kCrownRight) { model_string += "CrR"; } model_numbers++; } if (model_numbers == 0) model_string += "0"; dbg->push_back(model_string); } void RowScratchRegisters::Init(const RowInfo &row) { ri_ = &row; lmargin_ = 0; lindent_ = row.pix_ldistance; rmargin_ = 0; rindent_ = row.pix_rdistance; } LineType RowScratchRegisters::GetLineType() const { if (hypotheses_.empty()) return LT_UNKNOWN; bool has_start = false; bool has_body = false; for (int i = 0; i < hypotheses_.size(); i++) { switch (hypotheses_[i].ty) { case LT_START: has_start = true; break; case LT_BODY: has_body = true; break; default: tprintf("Encountered bad value in hypothesis list: %c\n", hypotheses_[i].ty); break; } } if (has_start && has_body) return LT_MULTIPLE; return has_start ? LT_START : LT_BODY; } LineType RowScratchRegisters::GetLineType(const ParagraphModel *model) const { if (hypotheses_.empty()) return LT_UNKNOWN; bool has_start = false; bool has_body = false; for (int i = 0; i < hypotheses_.size(); i++) { if (hypotheses_[i].model != model) continue; switch (hypotheses_[i].ty) { case LT_START: has_start = true; break; case LT_BODY: has_body = true; break; default: tprintf("Encountered bad value in hypothesis list: %c\n", hypotheses_[i].ty); break; } } if (has_start && has_body) return LT_MULTIPLE; return has_start ? LT_START : LT_BODY; } void RowScratchRegisters::SetStartLine() { LineType current_lt = GetLineType(); if (current_lt != LT_UNKNOWN && current_lt != LT_START) { tprintf("Trying to set a line to be START when it's already BODY.\n"); } if (current_lt == LT_UNKNOWN || current_lt == LT_BODY) { hypotheses_.push_back_new(LineHypothesis(LT_START, NULL)); } } void RowScratchRegisters::SetBodyLine() { LineType current_lt = GetLineType(); if (current_lt != LT_UNKNOWN && current_lt != LT_BODY) { tprintf("Trying to set a line to be BODY when it's already START.\n"); } if (current_lt == LT_UNKNOWN || current_lt == LT_START) { hypotheses_.push_back_new(LineHypothesis(LT_BODY, NULL)); } } void RowScratchRegisters::AddStartLine(const ParagraphModel *model) { hypotheses_.push_back_new(LineHypothesis(LT_START, model)); int old_idx = hypotheses_.get_index(LineHypothesis(LT_START, NULL)); if (old_idx >= 0) hypotheses_.remove(old_idx); } void RowScratchRegisters::AddBodyLine(const ParagraphModel *model) { hypotheses_.push_back_new(LineHypothesis(LT_BODY, model)); int old_idx = hypotheses_.get_index(LineHypothesis(LT_BODY, NULL)); if (old_idx >= 0) hypotheses_.remove(old_idx); } void RowScratchRegisters::StartHypotheses(SetOfModels *models) const { for (int h = 0; h < hypotheses_.size(); h++) { if (hypotheses_[h].ty == LT_START && StrongModel(hypotheses_[h].model)) models->push_back_new(hypotheses_[h].model); } } void RowScratchRegisters::StrongHypotheses(SetOfModels *models) const { for (int h = 0; h < hypotheses_.size(); h++) { if (StrongModel(hypotheses_[h].model)) models->push_back_new(hypotheses_[h].model); } } void RowScratchRegisters::NonNullHypotheses(SetOfModels *models) const { for (int h = 0; h < hypotheses_.size(); h++) { if (hypotheses_[h].model != NULL) models->push_back_new(hypotheses_[h].model); } } const ParagraphModel *RowScratchRegisters::UniqueStartHypothesis() const { if (hypotheses_.size() != 1 || hypotheses_[0].ty != LT_START) return NULL; return hypotheses_[0].model; } const ParagraphModel *RowScratchRegisters::UniqueBodyHypothesis() const { if (hypotheses_.size() != 1 || hypotheses_[0].ty != LT_BODY) return NULL; return hypotheses_[0].model; } // Discard any hypotheses whose model is not in the given list. void RowScratchRegisters::DiscardNonMatchingHypotheses( const SetOfModels &models) { if (models.empty()) return; for (int h = hypotheses_.size() - 1; h >= 0; h--) { if (!models.contains(hypotheses_[h].model)) { hypotheses_.remove(h); } } } // ============ Geometry based Paragraph Detection Algorithm ================= struct Cluster { Cluster() : center(0), count(0) {} Cluster(int cen, int num) : center(cen), count(num) {} int center; // The center of the cluster. int count; // The number of entries within the cluster. }; class SimpleClusterer { public: explicit SimpleClusterer(int max_cluster_width) : max_cluster_width_(max_cluster_width) {} void Add(int value) { values_.push_back(value); } int size() const { return values_.size(); } void GetClusters(GenericVector *clusters); private: int max_cluster_width_; GenericVectorEqEq values_; }; // Return the index of the cluster closest to value. int ClosestCluster(const GenericVector &clusters, int value) { int best_index = 0; for (int i = 0; i < clusters.size(); i++) { if (abs(value - clusters[i].center) < abs(value - clusters[best_index].center)) best_index = i; } return best_index; } void SimpleClusterer::GetClusters(GenericVector *clusters) { clusters->clear(); values_.sort(); for (int i = 0; i < values_.size();) { int orig_i = i; int lo = values_[i]; int hi = lo; while (++i < values_.size() && values_[i] <= lo + max_cluster_width_) { hi = values_[i]; } clusters->push_back(Cluster((hi + lo) / 2, i - orig_i)); } } // Calculate left- and right-indent tab stop values seen in // rows[row_start, row_end) given a tolerance of tolerance. void CalculateTabStops(GenericVector *rows, int row_start, int row_end, int tolerance, GenericVector *left_tabs, GenericVector *right_tabs) { if (!AcceptableRowArgs(0, 1, __func__, rows, row_start, row_end)) return; // First pass: toss all left and right indents into clusterers. SimpleClusterer initial_lefts(tolerance); SimpleClusterer initial_rights(tolerance); GenericVector initial_left_tabs; GenericVector initial_right_tabs; for (int i = row_start; i < row_end; i++) { initial_lefts.Add((*rows)[i].lindent_); initial_rights.Add((*rows)[i].rindent_); } initial_lefts.GetClusters(&initial_left_tabs); initial_rights.GetClusters(&initial_right_tabs); // Second pass: cluster only lines that are not "stray" // An example of a stray line is a page number -- a line whose start // and end tab-stops are far outside the typical start and end tab-stops // for the block. // Put another way, we only cluster data from lines whose start or end // tab stop is frequent. SimpleClusterer lefts(tolerance); SimpleClusterer rights(tolerance); // 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_); 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 ((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 && left_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 // start or body lines. // // Case 1: model->first_indent_ != model->body_indent_ // Differentiating the paragraph start lines from the paragraph body lines in // this case is easy, we just see how far each line is indented. // // Case 2: model->first_indent_ == model->body_indent_ // Here, we find end-of-paragraph lines by looking for "short lines." // What constitutes a "short line" changes depending on whether the text // ragged-right[left] or fully justified (aligned left and right). // // Case 2a: Ragged Right (or Left) text. (eop_threshold == 0) // We have a new paragraph it the first word would have at the end // of the previous line. // // Case 2b: Fully Justified. (eop_threshold > 0) // We mark a line as short (end of paragraph) if the offside indent // is greater than eop_threshold. void MarkRowsWithModel(GenericVector *rows, int row_start, int row_end, const ParagraphModel *model, bool ltr, int eop_threshold) { if (!AcceptableRowArgs(0, 0, __func__, rows, row_start, row_end)) return; for (int row = row_start; row < row_end; row++) { bool valid_first = ValidFirstLine(rows, row, model); bool valid_body = ValidBodyLine(rows, row, model); if (valid_first && !valid_body) { (*rows)[row].AddStartLine(model); } else if (valid_body && !valid_first) { (*rows)[row].AddBodyLine(model); } else if (valid_body && valid_first) { bool after_eop = (row == row_start); if (row > row_start) { if (eop_threshold > 0) { if (model->justification() == JUSTIFICATION_LEFT) { after_eop = (*rows)[row - 1].rindent_ > eop_threshold; } else { after_eop = (*rows)[row - 1].lindent_ > eop_threshold; } } else { after_eop = FirstWordWouldHaveFit((*rows)[row - 1], (*rows)[row], model->justification()); } } if (after_eop) { (*rows)[row].AddStartLine(model); } else { (*rows)[row].AddBodyLine(model); } } else { // Do nothing. Stray row. } } } // GeometricClassifierState holds all of the information we'll use while // trying to determine a paragraph model for the text lines in a block of // text: // + the rows under consideration [row_start, row_end) // + the common left- and right-indent tab stops // + does the block start out left-to-right or right-to-left // Further, this struct holds the data we amass for the (single) ParagraphModel // we'll assign to the text lines (assuming we get that far). struct GeometricClassifierState { GeometricClassifierState(int dbg_level, GenericVector *r, int r_start, int r_end) : debug_level(dbg_level), rows(r), row_start(r_start), row_end(r_end), margin(0) { 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; } void AssumeLeftJustification() { just = tesseract::JUSTIFICATION_LEFT; margin = (*rows)[row_start].lmargin_; } void AssumeRightJustification() { just = tesseract::JUSTIFICATION_RIGHT; margin = (*rows)[row_start].rmargin_; } // Align tabs are the tab stops the text is aligned to. const GenericVector &AlignTabs() const { if (just == tesseract::JUSTIFICATION_RIGHT) return right_tabs; return left_tabs; } // Offside tabs are the tab stops opposite the tabs used to align the text. // // Note that for a left-to-right text which is aligned to the right such as // this function comment, the offside tabs are the horizontal tab stops // marking the beginning of ("Note", "this" and "marking"). const GenericVector &OffsideTabs() const { if (just == tesseract::JUSTIFICATION_RIGHT) return left_tabs; return right_tabs; } // Return whether the i'th row extends from the leftmost left tab stop // to the right most right tab stop. bool IsFullRow(int i) const { return ClosestCluster(left_tabs, (*rows)[i].lindent_) == 0 && ClosestCluster(right_tabs, (*rows)[i].rindent_) == 0; } int AlignsideTabIndex(int row_idx) const { return ClosestCluster(AlignTabs(), (*rows)[row_idx].AlignsideIndent(just)); } // Given what we know about the paragraph justification (just), would the // first word of row_b have fit at the end of row_a? bool FirstWordWouldHaveFit(int row_a, int row_b) { return ::tesseract::FirstWordWouldHaveFit( (*rows)[row_a], (*rows)[row_b], just); } void PrintRows() const { PrintRowRange(*rows, row_start, row_end); } void Fail(int min_debug_level, const char *why) const { if (debug_level < min_debug_level) return; tprintf("# %s\n", why); PrintRows(); } ParagraphModel Model() const { return ParagraphModel(just, margin, first_indent, body_indent, tolerance); } // We print out messages with a debug level at least as great as debug_level. int debug_level; // The Geometric Classifier was asked to find a single paragraph model // to fit the text rows (*rows)[row_start, row_end) GenericVector *rows; int row_start; int row_end; // The amount by which we expect the text edge can vary and still be aligned. int tolerance; // Is the script in this text block left-to-right? // HORRIBLE ROUGH APPROXIMATION. TODO(eger): Improve bool ltr; // These left and right tab stops were determined to be the common tab // stops for the given text. GenericVector left_tabs; GenericVector right_tabs; // These are parameters we must determine to create a ParagraphModel. tesseract::ParagraphJustification just; int margin; int first_indent; int body_indent; // eop_threshold > 0 if the text is fully justified. See MarkRowsWithModel() int eop_threshold; }; // Given a section of text where strong textual clues did not help identifying // paragraph breaks, and for which the left and right indents have exactly // three tab stops between them, attempt to find the paragraph breaks based // solely on the outline of the text and whether the script is left-to-right. // // Algorithm Detail: // The selected rows are in the form of a rectangle except // for some number of "short lines" of the same length: // // (A1) xxxxxxxxxxxxx (B1) xxxxxxxxxxxx // xxxxxxxxxxx xxxxxxxxxx # A "short" line. // xxxxxxxxxxxxx xxxxxxxxxxxx // xxxxxxxxxxxxx xxxxxxxxxxxx // // We have a slightly different situation if the only short // line is at the end of the excerpt. // // (A2) xxxxxxxxxxxxx (B2) xxxxxxxxxxxx // xxxxxxxxxxxxx xxxxxxxxxxxx // xxxxxxxxxxxxx xxxxxxxxxxxx // xxxxxxxxxxx xxxxxxxxxx # A "short" line. // // We'll interpret these as follows based on the reasoning in the comment for // GeometricClassify(): // [script direction: first indent, body indent] // (A1) LtR: 2,0 RtL: 0,0 (B1) LtR: 0,0 RtL: 2,0 // (A2) LtR: 2,0 RtL: CrR (B2) LtR: CrL RtL: 2,0 void GeometricClassifyThreeTabStopTextBlock( int debug_level, GeometricClassifierState &s, ParagraphTheory *theory) { int num_rows = s.row_end - s.row_start; int num_full_rows = 0; int last_row_full = 0; for (int i = s.row_start; i < s.row_end; i++) { if (s.IsFullRow(i)) { num_full_rows++; if (i == s.row_end - 1) last_row_full++; } } if (num_full_rows < 0.7 * num_rows) { s.Fail(1, "Not enough full lines to know which lines start paras."); return; } // eop_threshold gets set if we're fully justified; see MarkRowsWithModel() s.eop_threshold = 0; if (s.ltr) { s.AssumeLeftJustification(); } else { s.AssumeRightJustification(); } if (debug_level > 0) { tprintf("# Not enough variety for clear outline classification. " "Guessing these are %s aligned based on script.\n", s.ltr ? "left" : "right"); s.PrintRows(); } if (s.AlignTabs().size() == 2) { // case A1 or A2 s.first_indent = s.AlignTabs()[1].center; s.body_indent = s.AlignTabs()[0].center; } else { // case B1 or B2 if (num_rows - 1 == num_full_rows - last_row_full) { // case B2 const ParagraphModel *model = s.ltr ? kCrownLeft : kCrownRight; (*s.rows)[s.row_start].AddStartLine(model); for (int i = s.row_start + 1; i < s.row_end; i++) { (*s.rows)[i].AddBodyLine(model); } return; } else { // case B1 s.first_indent = s.body_indent = s.AlignTabs()[0].center; s.eop_threshold = (s.OffsideTabs()[0].center + s.OffsideTabs()[1].center) / 2; } } const ParagraphModel *model = theory->AddModel(s.Model()); MarkRowsWithModel(s.rows, s.row_start, s.row_end, model, s.ltr, s.eop_threshold); return; } // This function is called if strong textual clues were not available, but // the caller hopes that the paragraph breaks will be super obvious just // by the outline of the text. // // The particularly difficult case is figuring out what's going on if you // don't have enough short paragraph end lines to tell us what's going on. // // For instance, let's say you have the following outline: // // (A1) xxxxxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxx // // Even if we know that the text is left-to-right and so will probably be // left-aligned, both of the following are possible texts: // // (A1a) 1. Here our list item // with two full lines. // 2. Here a second item. // 3. Here our third one. // // (A1b) so ends paragraph one. // Here starts another // paragraph we want to // read. This continues // // These examples are obvious from the text and should have been caught // by the StrongEvidenceClassify pass. However, for languages where we don't // have capital letters to go on (e.g. Hebrew, Arabic, Hindi, Chinese), // it's worth guessing that (A1b) is the correct interpretation if there are // far more "full" lines than "short" lines. void GeometricClassify(int debug_level, GenericVector *rows, int row_start, int row_end, ParagraphTheory *theory) { if (!AcceptableRowArgs(debug_level, 4, __func__, rows, row_start, row_end)) return; if (debug_level > 1) { tprintf("###############################################\n"); tprintf("##### GeometricClassify( rows[%d:%d) ) ####\n", row_start, row_end); tprintf("###############################################\n"); } RecomputeMarginsAndClearHypotheses(rows, row_start, row_end, 10); GeometricClassifierState s(debug_level, rows, row_start, row_end); if (s.left_tabs.size() > 2 && s.right_tabs.size() > 2) { s.Fail(2, "Too much variety for simple outline classification."); return; } if (s.left_tabs.size() <= 1 && s.right_tabs.size() <= 1) { s.Fail(1, "Not enough variety for simple outline classification."); return; } if (s.left_tabs.size() + s.right_tabs.size() == 3) { GeometricClassifyThreeTabStopTextBlock(debug_level, s, theory); return; } // At this point, we know that one side has at least two tab stops, and the // other side has one or two tab stops. // Left to determine: // (1) Which is the body indent and which is the first line indent? // (2) Is the text fully justified? // If one side happens to have three or more tab stops, assume that side // is opposite of the aligned side. if (s.right_tabs.size() > 2) { s.AssumeLeftJustification(); } else if (s.left_tabs.size() > 2) { s.AssumeRightJustification(); } else if (s.ltr) { // guess based on script direction s.AssumeLeftJustification(); } else { s.AssumeRightJustification(); } if (s.AlignTabs().size() == 2) { // For each tab stop on the aligned side, how many of them appear // to be paragraph start lines? [first lines] int firsts[2] = {0, 0}; // Count the first line as a likely paragraph start line. 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 (jam_packed && s.FirstWordWouldHaveFit(s.row_end - 1, s.row_end - 1)) { firsts[1 - s.AlignsideTabIndex(s.row_end - 1)]++; } int percent0firsts, percent1firsts; percent0firsts = (100 * firsts[0]) / s.AlignTabs()[0].count; percent1firsts = (100 * firsts[1]) / s.AlignTabs()[1].count; // TODO(eger): Tune these constants if necessary. if ((percent0firsts < 20 && 30 < percent1firsts) || percent0firsts + 30 < percent1firsts) { s.first_indent = s.AlignTabs()[1].center; s.body_indent = s.AlignTabs()[0].center; } else if ((percent1firsts < 20 && 30 < percent0firsts) || percent1firsts + 30 < percent0firsts) { s.first_indent = s.AlignTabs()[0].center; s.body_indent = s.AlignTabs()[1].center; } else { // Ambiguous! Probably lineated (poetry) if (debug_level > 1) { tprintf("# Cannot determine %s indent likely to start paragraphs.\n", s.just == tesseract::JUSTIFICATION_LEFT ? "left" : "right"); tprintf("# Indent of %d looks like a first line %d%% of the time.\n", s.AlignTabs()[0].center, percent0firsts); tprintf("# Indent of %d looks like a first line %d%% of the time.\n", s.AlignTabs()[1].center, percent1firsts); s.PrintRows(); } return; } } else { // There's only one tab stop for the "aligned to" side. s.first_indent = s.body_indent = s.AlignTabs()[0].center; } // At this point, we have our model. const ParagraphModel *model = theory->AddModel(s.Model()); // Now all we have to do is figure out if the text is fully justified or not. // eop_threshold: default to fully justified unless we see evidence below. // See description on MarkRowsWithModel() s.eop_threshold = (s.OffsideTabs()[0].center + s.OffsideTabs()[1].center) / 2; // If the text is not fully justified, re-set the eop_threshold to 0. if (s.AlignTabs().size() == 2) { // Paragraphs with a paragraph-start indent. for (int i = s.row_start; i < s.row_end - 1; i++) { if (ValidFirstLine(s.rows, i + 1, model) && !NearlyEqual(s.OffsideTabs()[0].center, (*s.rows)[i].OffsideIndent(s.just), s.tolerance)) { // We found a non-end-of-paragraph short line: not fully justified. s.eop_threshold = 0; break; } } } else { // Paragraphs with no paragraph-start indent. for (int i = s.row_start; i < s.row_end - 1; i++) { if (!s.FirstWordWouldHaveFit(i, i + 1) && !NearlyEqual(s.OffsideTabs()[0].center, (*s.rows)[i].OffsideIndent(s.just), s.tolerance)) { // We found a non-end-of-paragraph short line: not fully justified. s.eop_threshold = 0; break; } } } MarkRowsWithModel(rows, row_start, row_end, model, s.ltr, s.eop_threshold); } // =============== Implementation of ParagraphTheory ===================== const ParagraphModel *ParagraphTheory::AddModel(const ParagraphModel &model) { for (int i = 0; i < models_->size(); i++) { if ((*models_)[i]->Comparable(model)) return (*models_)[i]; } ParagraphModel *m = new ParagraphModel(model); models_->push_back(m); models_we_added_.push_back_new(m); return m; } void ParagraphTheory::DiscardUnusedModels(const SetOfModels &used_models) { for (int i = models_->size() - 1; i >= 0; i--) { ParagraphModel *m = (*models_)[i]; if (!used_models.contains(m) && models_we_added_.contains(m)) { models_->remove(i); models_we_added_.remove(models_we_added_.get_index(m)); delete m; } } } // Examine rows[start, end) and try to determine if an existing non-centered // paragraph model would fit them perfectly. If so, return a pointer to it. // If not, return NULL. const ParagraphModel *ParagraphTheory::Fits( const GenericVector *rows, int start, int end) const { for (int m = 0; m < models_->size(); m++) { const ParagraphModel *model = (*models_)[m]; if (model->justification() != JUSTIFICATION_CENTER && RowsFitModel(rows, start, end, model)) return model; } return NULL; } void ParagraphTheory::NonCenteredModels(SetOfModels *models) { for (int m = 0; m < models_->size(); m++) { const ParagraphModel *model = (*models_)[m]; if (model->justification() != JUSTIFICATION_CENTER) models->push_back_new(model); } } int ParagraphTheory::IndexOf(const ParagraphModel *model) const { for (int i = 0; i < models_->size(); i++) { if ((*models_)[i] == model) return i; } return -1; } bool ValidFirstLine(const GenericVector *rows, int row, const ParagraphModel *model) { if (!StrongModel(model)) { tprintf("ValidFirstLine() should only be called with strong models!\n"); } return StrongModel(model) && model->ValidFirstLine( (*rows)[row].lmargin_, (*rows)[row].lindent_, (*rows)[row].rindent_, (*rows)[row].rmargin_); } bool ValidBodyLine(const GenericVector *rows, int row, const ParagraphModel *model) { if (!StrongModel(model)) { tprintf("ValidBodyLine() should only be called with strong models!\n"); } return StrongModel(model) && model->ValidBodyLine( (*rows)[row].lmargin_, (*rows)[row].lindent_, (*rows)[row].rindent_, (*rows)[row].rmargin_); } bool CrownCompatible(const GenericVector *rows, int a, int b, const ParagraphModel *model) { if (model != kCrownRight && model != kCrownLeft) { tprintf("CrownCompatible() should only be called with crown models!\n"); return false; } RowScratchRegisters &row_a = (*rows)[a]; RowScratchRegisters &row_b = (*rows)[b]; if (model == kCrownRight) { return NearlyEqual(row_a.rindent_ + row_a.rmargin_, row_b.rindent_ + row_b.rmargin_, Epsilon(row_a.ri_->average_interword_space)); } return NearlyEqual(row_a.lindent_ + row_a.lmargin_, row_b.lindent_ + row_b.lmargin_, Epsilon(row_a.ri_->average_interword_space)); } // =============== Implementation of ParagraphModelSmearer ==================== ParagraphModelSmearer::ParagraphModelSmearer( GenericVector *rows, int row_start, int row_end, ParagraphTheory *theory) : theory_(theory), rows_(rows), row_start_(row_start), row_end_(row_end) { if (!AcceptableRowArgs(0, 0, __func__, rows, row_start, row_end)) { row_start_ = 0; row_end_ = 0; return; } SetOfModels no_models; for (int row = row_start - 1; row <= row_end; row++) { open_models_.push_back(no_models); } } // see paragraphs_internal.h void ParagraphModelSmearer::CalculateOpenModels(int row_start, int row_end) { SetOfModels no_models; if (row_start < row_start_) row_start = row_start_; if (row_end > row_end_) row_end = row_end_; for (int row = (row_start > 0) ? row_start - 1 : row_start; row < row_end; row++) { if ((*rows_)[row].ri_->num_words == 0) { OpenModels(row + 1) = no_models; } else { SetOfModels &opened = OpenModels(row); (*rows_)[row].StartHypotheses(&opened); // Which models survive the transition from row to row + 1? SetOfModels still_open; for (int m = 0; m < opened.size(); m++) { if (ValidFirstLine(rows_, row, opened[m]) || ValidBodyLine(rows_, row, opened[m])) { // This is basic filtering; we check likely paragraph starty-ness down // below in Smear() -- you know, whether the first word would have fit // and such. still_open.push_back_new(opened[m]); } } OpenModels(row + 1) = still_open; } } } // see paragraphs_internal.h void ParagraphModelSmearer::Smear() { CalculateOpenModels(row_start_, row_end_); // For each row which we're unsure about (that is, it is LT_UNKNOWN or // we have multiple LT_START hypotheses), see if there's a model that // was recently used (an "open" model) which might model it well. for (int i = row_start_; i < row_end_; i++) { RowScratchRegisters &row = (*rows_)[i]; if (row.ri_->num_words == 0) continue; // Step One: // Figure out if there are "open" models which are left-alined or // right-aligned. This is important for determining whether the // "first" word in a row would fit at the "end" of the previous row. bool left_align_open = false; bool right_align_open = false; for (int m = 0; m < OpenModels(i).size(); m++) { switch (OpenModels(i)[m]->justification()) { case JUSTIFICATION_LEFT: left_align_open = true; break; case JUSTIFICATION_RIGHT: right_align_open = true; break; default: left_align_open = right_align_open = true; } } // Step Two: // Use that knowledge to figure out if this row is likely to // start a paragraph. bool likely_start; if (i == 0) { likely_start = true; } else { if ((left_align_open && right_align_open) || (!left_align_open && !right_align_open)) { likely_start = LikelyParagraphStart((*rows_)[i - 1], row, JUSTIFICATION_LEFT) || LikelyParagraphStart((*rows_)[i - 1], row, JUSTIFICATION_RIGHT); } else if (left_align_open) { likely_start = LikelyParagraphStart((*rows_)[i - 1], row, JUSTIFICATION_LEFT); } else { likely_start = LikelyParagraphStart((*rows_)[i - 1], row, JUSTIFICATION_RIGHT); } } // Step Three: // If this text line seems like an obvious first line of an // open model, or an obvious continuation of an existing // modelled paragraph, mark it up. if (likely_start) { // Add Start Hypotheses for all Open models that fit. for (int m = 0; m < OpenModels(i).size(); m++) { if (ValidFirstLine(rows_, i, OpenModels(i)[m])) { row.AddStartLine(OpenModels(i)[m]); } } } else { // Add relevant body line hypotheses. SetOfModels last_line_models; if (i > 0) { (*rows_)[i - 1].StrongHypotheses(&last_line_models); } else { theory_->NonCenteredModels(&last_line_models); } for (int m = 0; m < last_line_models.size(); m++) { const ParagraphModel *model = last_line_models[m]; if (ValidBodyLine(rows_, i, model)) row.AddBodyLine(model); } } // Step Four: // If we're still quite unsure about this line, go through all // models in our theory and see if this row could be the start // of any of our models. if (row.GetLineType() == LT_UNKNOWN || (row.GetLineType() == LT_START && !row.UniqueStartHypothesis())) { SetOfModels all_models; theory_->NonCenteredModels(&all_models); for (int m = 0; m < all_models.size(); m++) { if (ValidFirstLine(rows_, i, all_models[m])) { row.AddStartLine(all_models[m]); } } } // Step Five: // Since we may have updated the hypotheses about this row, we need // to recalculate the Open models for the rest of rows[i + 1, row_end) if (row.GetLineType() != LT_UNKNOWN) { CalculateOpenModels(i + 1, row_end_); } } } // ================ Main Paragraph Detection Algorithm ======================= // Find out what ParagraphModels are actually used, and discard any // that are not. void DiscardUnusedModels(const GenericVector &rows, ParagraphTheory *theory) { SetOfModels used_models; for (int i = 0; i < rows.size(); i++) { rows[i].StrongHypotheses(&used_models); } theory->DiscardUnusedModels(used_models); } // DowngradeWeakestToCrowns: // Forget any flush-{left, right} models unless we see two or more // of them in sequence. // // In pass 3, we start to classify even flush-left paragraphs (paragraphs // where the first line and body indent are the same) as having proper Models. // This is generally dangerous, since if you start imagining that flush-left // is a typical paragraph model when it is not, it will lead you to chop normal // indented paragraphs in the middle whenever a sentence happens to start on a // new line (see "This" above). What to do? // What we do is to take any paragraph which is flush left and is not // preceded by another paragraph of the same model and convert it to a "Crown" // paragraph. This is a weak pseudo-ParagraphModel which is a placeholder // for later. It means that the paragraph is flush, but it would be desirable // to mark it as the same model as following text if it fits. This downgrade // FlushLeft -> CrownLeft -> Model of following paragraph. Means that we // avoid making flush left Paragraph Models whenever we see a top-of-the-page // half-of-a-paragraph. and instead we mark it the same as normal body text. // // Implementation: // // Comb backwards through the row scratch registers, and turn any // sequences of body lines of equivalent type abutted against the beginning // or a body or start line of a different type into a crown paragraph. void DowngradeWeakestToCrowns(int debug_level, ParagraphTheory *theory, GenericVector *rows) { int start; for (int end = rows->size(); end > 0; end = start) { // Search back for a body line of a unique type. const ParagraphModel *model = NULL; while (end > 0 && (model = (*rows)[end - 1].UniqueBodyHypothesis()) == NULL) { end--; } if (end == 0) break; start = end - 1; while (start >= 0 && (*rows)[start].UniqueBodyHypothesis() == model) { start--; // walk back to the first line that is not the same body type. } if (start >= 0 && (*rows)[start].UniqueStartHypothesis() == model && StrongModel(model) && NearlyEqual(model->first_indent(), model->body_indent(), model->tolerance())) { start--; } start++; // Now rows[start, end) is a sequence of unique body hypotheses of model. if (StrongModel(model) && model->justification() == JUSTIFICATION_CENTER) continue; if (!StrongModel(model)) { while (start > 0 && CrownCompatible(rows, start - 1, start, model)) start--; } if (start == 0 || (!StrongModel(model)) || (StrongModel(model) && !ValidFirstLine(rows, start - 1, model))) { // crownify rows[start, end) const ParagraphModel *crown_model = model; if (StrongModel(model)) { if (model->justification() == JUSTIFICATION_LEFT) crown_model = kCrownLeft; else crown_model = kCrownRight; } (*rows)[start].SetUnknown(); (*rows)[start].AddStartLine(crown_model); for (int row = start + 1; row < end; row++) { (*rows)[row].SetUnknown(); (*rows)[row].AddBodyLine(crown_model); } } } DiscardUnusedModels(*rows, theory); } // Clear all hypotheses about lines [start, end) and reset margins. // // The empty space between the left of a row and the block boundary (and // similarly for the right) is split into two pieces: margin and indent. // In initial processing, we assume the block is tight and the margin for // all lines is set to zero. However, if our first pass does not yield // models for everything, it may be due to an inset paragraph like a // block-quote. In that case, we make a second pass over that unmarked // section of the page and reset the "margin" portion of the empty space // to the common amount of space at the ends of the lines under consid- // eration. This would be equivalent to percentile set to 0. However, // sometimes we have a single character sticking out in the right margin // of a text block (like the 'r' in 'for' on line 3 above), and we can // really just ignore it as an outlier. To express this, we allow the // user to specify the percentile (0..100) of indent values to use as // the common margin for each row in the run of rows[start, end). void RecomputeMarginsAndClearHypotheses( GenericVector *rows, int start, int end, int percentile) { if (!AcceptableRowArgs(0, 0, __func__, rows, start, end)) return; int lmin, lmax, rmin, rmax; lmin = lmax = (*rows)[start].lmargin_ + (*rows)[start].lindent_; rmin = rmax = (*rows)[start].rmargin_ + (*rows)[start].rindent_; for (int i = start; i < end; i++) { RowScratchRegisters &sr = (*rows)[i]; sr.SetUnknown(); if (sr.ri_->num_words == 0) continue; UpdateRange(sr.lmargin_ + sr.lindent_, &lmin, &lmax); UpdateRange(sr.rmargin_ + sr.rindent_, &rmin, &rmax); } STATS lefts(lmin, lmax + 1); STATS rights(rmin, rmax + 1); for (int i = start; i < end; i++) { RowScratchRegisters &sr = (*rows)[i]; if (sr.ri_->num_words == 0) continue; lefts.add(sr.lmargin_ + sr.lindent_, 1); rights.add(sr.rmargin_ + sr.rindent_, 1); } int ignorable_left = lefts.ile(ClipToRange(percentile, 0, 100) / 100.0); int ignorable_right = rights.ile(ClipToRange(percentile, 0, 100) / 100.0); for (int i = start; i < end; i++) { RowScratchRegisters &sr = (*rows)[i]; int ldelta = ignorable_left - sr.lmargin_; sr.lmargin_ += ldelta; sr.lindent_ -= ldelta; int rdelta = ignorable_right - sr.rmargin_; sr.rmargin_ += rdelta; sr.rindent_ -= rdelta; } } // 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; 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) { spacing_widths.add(rows[i].ri_->average_interword_space, 1); } } 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 // the end of the before line (knowing which way the text is aligned and read). bool FirstWordWouldHaveFit(const RowScratchRegisters &before, const RowScratchRegisters &after, tesseract::ParagraphJustification justification) { if (before.ri_->num_words == 0 || after.ri_->num_words == 0) return true; if (justification == JUSTIFICATION_UNKNOWN) { tprintf("Don't call FirstWordWouldHaveFit(r, s, JUSTIFICATION_UNKNOWN).\n"); } int available_space; if (justification == JUSTIFICATION_CENTER) { available_space = before.lindent_ + before.rindent_; } else { available_space = before.OffsideIndent(justification); } available_space -= before.ri_->average_interword_space; if (before.ri_->ltr) return after.ri_->lword_box.width() < available_space; return after.ri_->rword_box.width() < available_space; } // Return whether the first word on the after line can fit in the space at // the end of the before line (not knowing which way the text goes) in a left // or right alignemnt. bool FirstWordWouldHaveFit(const RowScratchRegisters &before, const RowScratchRegisters &after) { if (before.ri_->num_words == 0 || after.ri_->num_words == 0) return true; int available_space = before.lindent_; if (before.rindent_ > available_space) available_space = before.rindent_; available_space -= before.ri_->average_interword_space; if (before.ri_->ltr) return after.ri_->lword_box.width() < available_space; return after.ri_->rword_box.width() < available_space; } bool TextSupportsBreak(const RowScratchRegisters &before, const RowScratchRegisters &after) { if (before.ri_->ltr) { return before.ri_->rword_likely_ends_idea && after.ri_->lword_likely_starts_idea; } else { return before.ri_->lword_likely_ends_idea && after.ri_->rword_likely_starts_idea; } } bool LikelyParagraphStart(const RowScratchRegisters &before, const RowScratchRegisters &after) { return before.ri_->num_words == 0 || (FirstWordWouldHaveFit(before, after) && TextSupportsBreak(before, after)); } bool LikelyParagraphStart(const RowScratchRegisters &before, const RowScratchRegisters &after, tesseract::ParagraphJustification j) { return before.ri_->num_words == 0 || (FirstWordWouldHaveFit(before, after, j) && TextSupportsBreak(before, after)); } // Examine rows[start, end) and try to determine what sort of ParagraphModel // would fit them as a single paragraph. // If we can't produce a unique model justification_ = JUSTIFICATION_UNKNOWN. // If the rows given could be a consistent start to a paragraph, set *consistent // true. ParagraphModel InternalParagraphModelByOutline( const GenericVector *rows, int start, int end, int tolerance, bool *consistent) { int ltr_line_count = 0; for (int i = start; i < end; i++) { ltr_line_count += static_cast((*rows)[i].ri_->ltr); } bool ltr = (ltr_line_count >= (end - start) / 2); *consistent = true; if (!AcceptableRowArgs(0, 2, __func__, rows, start, end)) return ParagraphModel(); // Ensure the caller only passed us a region with a common rmargin and // lmargin. int lmargin = (*rows)[start].lmargin_; int rmargin = (*rows)[start].rmargin_; int lmin, lmax, rmin, rmax, cmin, cmax; lmin = lmax = (*rows)[start + 1].lindent_; rmin = rmax = (*rows)[start + 1].rindent_; cmin = cmax = 0; for (int i = start + 1; i < end; i++) { if ((*rows)[i].lmargin_ != lmargin || (*rows)[i].rmargin_ != rmargin) { tprintf("Margins don't match! Software error.\n"); *consistent = false; return ParagraphModel(); } UpdateRange((*rows)[i].lindent_, &lmin, &lmax); UpdateRange((*rows)[i].rindent_, &rmin, &rmax); UpdateRange((*rows)[i].rindent_ - (*rows)[i].lindent_, &cmin, &cmax); } int ldiff = lmax - lmin; int rdiff = rmax - rmin; int cdiff = cmax - cmin; if (rdiff > tolerance && ldiff > tolerance) { if (cdiff < tolerance * 2) { if (end - start < 3) return ParagraphModel(); return ParagraphModel(JUSTIFICATION_CENTER, 0, 0, 0, tolerance); } *consistent = false; return ParagraphModel(); } if (end - start < 3) // Don't return a model for two line paras. return ParagraphModel(); // These booleans keep us from saying something is aligned left when the body // left variance is too large. bool body_admits_left_alignment = ldiff < tolerance; bool body_admits_right_alignment = rdiff < tolerance; ParagraphModel left_model = ParagraphModel(JUSTIFICATION_LEFT, lmargin, (*rows)[start].lindent_, (lmin + lmax) / 2, tolerance); ParagraphModel right_model = ParagraphModel(JUSTIFICATION_RIGHT, rmargin, (*rows)[start].rindent_, (rmin + rmax) / 2, tolerance); // These booleans keep us from having an indent on the "wrong side" for the // first line. bool text_admits_left_alignment = ltr || left_model.is_flush(); bool text_admits_right_alignment = !ltr || right_model.is_flush(); // At least one of the edges is less than tolerance in variance. // If the other is obviously ragged, it can't be the one aligned to. // [Note the last line is included in this raggedness.] if (tolerance < rdiff) { if (body_admits_left_alignment && text_admits_left_alignment) return left_model; *consistent = false; return ParagraphModel(); } if (tolerance < ldiff) { if (body_admits_right_alignment && text_admits_right_alignment) return right_model; *consistent = false; return ParagraphModel(); } // At this point, we know the body text doesn't vary much on either side. // If the first line juts out oddly in one direction or the other, // that likely indicates the side aligned to. int first_left = (*rows)[start].lindent_; int first_right = (*rows)[start].rindent_; if (ltr && body_admits_left_alignment && (first_left < lmin || first_left > lmax)) return left_model; if (!ltr && body_admits_right_alignment && (first_right < rmin || first_right > rmax)) return right_model; *consistent = false; return ParagraphModel(); } // Examine rows[start, end) and try to determine what sort of ParagraphModel // would fit them as a single paragraph. If nothing fits, // justification_ = JUSTIFICATION_UNKNOWN and print the paragraph to debug // output if we're debugging. ParagraphModel ParagraphModelByOutline( int debug_level, const GenericVector *rows, int start, int end, int tolerance) { bool unused_consistent; ParagraphModel retval = InternalParagraphModelByOutline( rows, start, end, tolerance, &unused_consistent); if (debug_level >= 2 && retval.justification() == JUSTIFICATION_UNKNOWN) { tprintf("Could not determine a model for this paragraph:\n"); PrintRowRange(*rows, start, end); } return retval; } // Do rows[start, end) form a single instance of the given paragraph model? bool RowsFitModel(const GenericVector *rows, int start, int end, const ParagraphModel *model) { if (!AcceptableRowArgs(0, 1, __func__, rows, start, end)) return false; if (!ValidFirstLine(rows, start, model)) return false; for (int i = start + 1 ; i < end; i++) { if (!ValidBodyLine(rows, i, model)) return false; } return true; } // Examine rows[row_start, row_end) as an independent section of text, // and mark rows that are exceptionally clear as start-of-paragraph // and paragraph-body lines. // // We presume that any lines surrounding rows[row_start, row_end) may // have wildly different paragraph models, so we don't key any data off // of those lines. // // We only take the very strongest signals, as we don't want to get // confused and marking up centered text, poetry, or source code as // clearly part of a typical paragraph. void MarkStrongEvidence(GenericVector *rows, int row_start, int row_end) { // Record patently obvious body text. for (int i = row_start + 1; i < row_end; i++) { const RowScratchRegisters &prev = (*rows)[i - 1]; RowScratchRegisters &curr = (*rows)[i]; tesseract::ParagraphJustification typical_justification = prev.ri_->ltr ? JUSTIFICATION_LEFT : JUSTIFICATION_RIGHT; if (!curr.ri_->rword_likely_starts_idea && !curr.ri_->lword_likely_starts_idea && !FirstWordWouldHaveFit(prev, curr, typical_justification)) { curr.SetBodyLine(); } } // Record patently obvious start paragraph lines. // // It's an extremely good signal of the start of a paragraph that // the first word would have fit on the end of the previous line. // However, applying just that signal would have us mark random // start lines of lineated text (poetry and source code) and some // centered headings as paragraph start lines. Therefore, we use // a second qualification for a paragraph start: Not only should // the first word of this line have fit on the previous line, // but also, this line should go full to the right of the block, // disallowing a subsequent word from having fit on this line. // First row: { RowScratchRegisters &curr = (*rows)[row_start]; RowScratchRegisters &next = (*rows)[row_start + 1]; tesseract::ParagraphJustification j = curr.ri_->ltr ? JUSTIFICATION_LEFT : JUSTIFICATION_RIGHT; if (curr.GetLineType() == LT_UNKNOWN && !FirstWordWouldHaveFit(curr, next, j) && (curr.ri_->lword_likely_starts_idea || curr.ri_->rword_likely_starts_idea)) { curr.SetStartLine(); } } // Middle rows for (int i = row_start + 1; i < row_end - 1; i++) { RowScratchRegisters &prev = (*rows)[i - 1]; RowScratchRegisters &curr = (*rows)[i]; RowScratchRegisters &next = (*rows)[i + 1]; tesseract::ParagraphJustification j = curr.ri_->ltr ? JUSTIFICATION_LEFT : JUSTIFICATION_RIGHT; if (curr.GetLineType() == LT_UNKNOWN && !FirstWordWouldHaveFit(curr, next, j) && LikelyParagraphStart(prev, curr, j)) { curr.SetStartLine(); } } // Last row { // the short circuit at the top means we have at least two lines. RowScratchRegisters &prev = (*rows)[row_end - 2]; RowScratchRegisters &curr = (*rows)[row_end - 1]; tesseract::ParagraphJustification j = curr.ri_->ltr ? JUSTIFICATION_LEFT : JUSTIFICATION_RIGHT; if (curr.GetLineType() == LT_UNKNOWN && !FirstWordWouldHaveFit(curr, curr, j) && LikelyParagraphStart(prev, curr, j)) { curr.SetStartLine(); } } } // Look for sequences of a start line followed by some body lines in // rows[row_start, row_end) and create ParagraphModels for them if // they seem coherent. void ModelStrongEvidence(int debug_level, GenericVector *rows, int row_start, int row_end, bool allow_flush_models, ParagraphTheory *theory) { if (!AcceptableRowArgs(debug_level, 2, __func__, rows, row_start, row_end)) return; int start = row_start; while (start < row_end) { while (start < row_end && (*rows)[start].GetLineType() != LT_START) start++; if (start >= row_end - 1) break; int tolerance = Epsilon((*rows)[start + 1].ri_->average_interword_space); int end = start; ParagraphModel last_model; bool next_consistent; do { ++end; // rows[row, end) was consistent. // If rows[row, end + 1) is not consistent, // just model rows[row, end) if (end < row_end - 1) { RowScratchRegisters &next = (*rows)[end]; LineType lt = next.GetLineType(); next_consistent = lt == LT_BODY || (lt == LT_UNKNOWN && !FirstWordWouldHaveFit((*rows)[end - 1], (*rows)[end])); } else { next_consistent = false; } if (next_consistent) { ParagraphModel next_model = InternalParagraphModelByOutline( rows, start, end + 1, tolerance, &next_consistent); if (((*rows)[start].ri_->ltr && last_model.justification() == JUSTIFICATION_LEFT && next_model.justification() != JUSTIFICATION_LEFT) || (!(*rows)[start].ri_->ltr && last_model.justification() == JUSTIFICATION_RIGHT && next_model.justification() != JUSTIFICATION_RIGHT)) { next_consistent = false; } last_model = next_model; } else { next_consistent = false; } } while (next_consistent && end < row_end); // At this point, rows[start, end) looked like it could have been a // single paragraph. If we can make a good ParagraphModel for it, // do so and mark this sequence with that model. if (end > start + 1) { // emit a new paragraph if we have more than one line. const ParagraphModel *model = NULL; ParagraphModel new_model = ParagraphModelByOutline( debug_level, rows, start, end, Epsilon(InterwordSpace(*rows, start, end))); if (new_model.justification() == JUSTIFICATION_UNKNOWN) { // couldn't create a good model, oh well. } else if (new_model.is_flush()) { if (end == start + 2) { // It's very likely we just got two paragraph starts in a row. end = start + 1; } else if (start == row_start) { // Mark this as a Crown. if (new_model.justification() == JUSTIFICATION_LEFT) { model = kCrownLeft; } else { model = kCrownRight; } } else if (allow_flush_models) { model = theory->AddModel(new_model); } } else { model = theory->AddModel(new_model); } if (model) { (*rows)[start].AddStartLine(model); for (int i = start + 1; i < end; i++) { (*rows)[i].AddBodyLine(model); } } } start = end; } } // We examine rows[row_start, row_end) and do the following: // (1) Clear all existing hypotheses for the rows being considered. // (2) Mark up any rows as exceptionally likely to be paragraph starts // or paragraph body lines as such using both geometric and textual // clues. // (3) Form models for any sequence of start + continuation lines. // (4) Smear the paragraph models to cover surrounding text. void StrongEvidenceClassify(int debug_level, GenericVector *rows, int row_start, int row_end, ParagraphTheory *theory) { if (!AcceptableRowArgs(debug_level, 2, __func__, rows, row_start, row_end)) return; if (debug_level > 1) { tprintf("#############################################\n"); tprintf("# StrongEvidenceClassify( rows[%d:%d) )\n", row_start, row_end); tprintf("#############################################\n"); } RecomputeMarginsAndClearHypotheses(rows, row_start, row_end, 10); MarkStrongEvidence(rows, row_start, row_end); DebugDump(debug_level > 2, "Initial strong signals.", *theory, *rows); // Create paragraph models. ModelStrongEvidence(debug_level, rows, row_start, row_end, false, theory); DebugDump(debug_level > 2, "Unsmeared hypotheses.s.", *theory, *rows); // At this point, some rows are marked up as paragraphs with model numbers, // and some rows are marked up as either LT_START or LT_BODY. Now let's // smear any good paragraph hypotheses forward and backward. ParagraphModelSmearer smearer(rows, row_start, row_end, theory); smearer.Smear(); } void SeparateSimpleLeaderLines(GenericVector *rows, int row_start, int row_end, ParagraphTheory *theory) { for (int i = row_start + 1; i < row_end - 1; i++) { if ((*rows)[i - 1].ri_->has_leaders && (*rows)[i].ri_->has_leaders && (*rows)[i + 1].ri_->has_leaders) { const ParagraphModel *model = theory->AddModel( ParagraphModel(JUSTIFICATION_UNKNOWN, 0, 0, 0, 0)); (*rows)[i].AddStartLine(model); } } } // Collect sequences of unique hypotheses in row registers and create proper // paragraphs for them, referencing the paragraphs in row_owners. void ConvertHypothesizedModelRunsToParagraphs( int debug_level, const GenericVector &rows, GenericVector *row_owners, ParagraphTheory *theory) { int end = rows.size(); int start; for (; end > 0; end = start) { start = end - 1; const ParagraphModel *model = NULL; // TODO(eger): Be smarter about dealing with multiple hypotheses. bool single_line_paragraph = false; SetOfModels models; rows[start].NonNullHypotheses(&models); if (models.size() > 0) { model = models[0]; if (rows[start].GetLineType(model) != LT_BODY) single_line_paragraph = true; } if (model && !single_line_paragraph) { // walk back looking for more body lines and then a start line. while (--start > 0 && rows[start].GetLineType(model) == LT_BODY) { // do nothing } if (start < 0 || rows[start].GetLineType(model) != LT_START) { model = NULL; } } if (model == NULL) { continue; } // rows[start, end) should be a paragraph. PARA *p = new PARA(); if (model == kCrownLeft || model == kCrownRight) { p->is_very_first_or_continuation = true; // Crown paragraph. // If we can find an existing ParagraphModel that fits, use it, // else create a new one. for (int row = end; row < rows.size(); row++) { if ((*row_owners)[row] && (ValidBodyLine(&rows, start, (*row_owners)[row]->model) && (start == 0 || ValidFirstLine(&rows, start, (*row_owners)[row]->model)))) { model = (*row_owners)[row]->model; break; } } if (model == kCrownLeft) { // No subsequent model fits, so cons one up. model = theory->AddModel(ParagraphModel( JUSTIFICATION_LEFT, rows[start].lmargin_ + rows[start].lindent_, 0, 0, Epsilon(rows[start].ri_->average_interword_space))); } else if (model == kCrownRight) { // No subsequent model fits, so cons one up. model = theory->AddModel(ParagraphModel( JUSTIFICATION_RIGHT, rows[start].rmargin_ + rows[start].rmargin_, 0, 0, Epsilon(rows[start].ri_->average_interword_space))); } } rows[start].SetUnknown(); rows[start].AddStartLine(model); for (int i = start + 1; i < end; i++) { rows[i].SetUnknown(); rows[i].AddBodyLine(model); } p->model = model; p->has_drop_cap = rows[start].ri_->has_drop_cap; p->is_list_item = model->justification() == JUSTIFICATION_RIGHT ? rows[start].ri_->rword_indicates_list_item : rows[start].ri_->lword_indicates_list_item; for (int row = start; row < end; row++) { if ((*row_owners)[row] != NULL) { tprintf("Memory leak! ConvertHypothesizeModelRunsToParagraphs() called " "more than once!\n"); } (*row_owners)[row] = p; } } } struct Interval { Interval() : begin(0), end(0) {} Interval(int b, int e) : begin(b), end(e) {} int begin; int end; }; // Return whether rows[row] appears to be stranded, meaning that the evidence // for this row is very weak due to context. For instance, two lines of source // code may happen to be indented at the same tab vector as body text starts, // leading us to think they are two start-of-paragraph lines. This is not // optimal. However, we also don't want to mark a sequence of short dialog // as "weak," so our heuristic is: // (1) If a line is surrounded by lines of unknown type, it's weak. // (2) If two lines in a row are start lines for a given paragraph type, but // after that the same paragraph type does not continue, they're weak. bool RowIsStranded(const GenericVector &rows, int row) { SetOfModels row_models; rows[row].StrongHypotheses(&row_models); for (int m = 0; m < row_models.size(); m++) { bool all_starts = rows[row].GetLineType(); int run_length = 1; bool continues = true; for (int i = row - 1; i >= 0 && continues; i--) { SetOfModels models; rows[i].NonNullHypotheses(&models); switch (rows[i].GetLineType(row_models[m])) { case LT_START: run_length++; break; case LT_MULTIPLE: // explicit fall-through case LT_BODY: run_length++; all_starts = false; break; case LT_UNKNOWN: // explicit fall-through default: continues = false; } } continues = true; for (int i = row + 1; i < rows.size() && continues; i++) { SetOfModels models; rows[i].NonNullHypotheses(&models); switch (rows[i].GetLineType(row_models[m])) { case LT_START: run_length++; break; case LT_MULTIPLE: // explicit fall-through case LT_BODY: run_length++; all_starts = false; break; case LT_UNKNOWN: // explicit fall-through default: continues = false; } } if (run_length > 2 || (!all_starts && run_length > 1)) return false; } return true; } // Go through rows[row_start, row_end) and gather up sequences that need better // classification. // + Sequences of non-empty rows without hypotheses. // + Crown paragraphs not immediately followed by a strongly modeled line. // + Single line paragraphs surrounded by text that doesn't match the // model. void LeftoverSegments(const GenericVector &rows, GenericVector *to_fix, int row_start, int row_end) { to_fix->clear(); for (int i = row_start; i < row_end; i++) { bool needs_fixing = false; SetOfModels models; SetOfModels models_w_crowns; rows[i].StrongHypotheses(&models); rows[i].NonNullHypotheses(&models_w_crowns); if (models.empty() && models_w_crowns.size() > 0) { // Crown paragraph. Is it followed by a modeled line? for (int end = i + 1; end < rows.size(); end++) { SetOfModels end_models; SetOfModels strong_end_models; rows[end].NonNullHypotheses(&end_models); rows[end].StrongHypotheses(&strong_end_models); if (end_models.size() == 0) { needs_fixing = true; break; } else if (strong_end_models.size() > 0) { needs_fixing = false; break; } } } else if (models.empty() && rows[i].ri_->num_words > 0) { // No models at all. needs_fixing = true; } if (!needs_fixing && !models.empty()) { needs_fixing = RowIsStranded(rows, i); } if (needs_fixing) { if (!to_fix->empty() && to_fix->back().end == i - 1) to_fix->back().end = i; else to_fix->push_back(Interval(i, i)); } } // Convert inclusive intervals to half-open intervals. for (int i = 0; i < to_fix->size(); i++) { (*to_fix)[i].end = (*to_fix)[i].end + 1; } } // Given a set of row_owners pointing to PARAs or NULL (no paragraph known), // normalize each row_owner to point to an actual PARA, and output the // paragraphs in order onto paragraphs. void CanonicalizeDetectionResults( GenericVector *row_owners, PARA_LIST *paragraphs) { GenericVector &rows = *row_owners; paragraphs->clear(); PARA_IT out(paragraphs); PARA *formerly_null = NULL; for (int i = 0; i < rows.size(); i++) { if (rows[i] == NULL) { if (i == 0 || rows[i - 1] != formerly_null) { rows[i] = formerly_null = new PARA(); } else { rows[i] = formerly_null; continue; } } else if (i > 0 && rows[i - 1] == rows[i]) { continue; } out.add_after_then_move(rows[i]); } } // Main entry point for Paragraph Detection Algorithm. // // Given a set of equally spaced textlines (described by row_infos), // Split them into paragraphs. // // Output: // row_owners - one pointer for each row, to the paragraph it belongs to. // paragraphs - this is the actual list of PARA objects. // models - the list of paragraph models referenced by the PARA objects. // caller is responsible for deleting the models. void DetectParagraphs(int debug_level, GenericVector *row_infos, GenericVector *row_owners, PARA_LIST *paragraphs, GenericVector *models) { GenericVector rows; ParagraphTheory theory(models); // Initialize row_owners to be a bunch of NULL pointers. row_owners->init_to_size(row_infos->size(), NULL); // Set up row scratch registers for the main algorithm. rows.init_to_size(row_infos->size(), RowScratchRegisters()); for (int i = 0; i < row_infos->size(); i++) { rows[i].Init((*row_infos)[i]); } // Pass 1: // Detect sequences of lines that all contain leader dots (.....) // These are likely Tables of Contents. If there are three text lines in // a row with leader dots, it's pretty safe to say the middle one should // be a paragraph of its own. SeparateSimpleLeaderLines(&rows, 0, rows.size(), &theory); DebugDump(debug_level > 1, "End of Pass 1", theory, rows); GenericVector leftovers; LeftoverSegments(rows, &leftovers, 0, rows.size()); for (int i = 0; i < leftovers.size(); i++) { // Pass 2a: // Find any strongly evidenced start-of-paragraph lines. If they're // followed by two lines that look like body lines, make a paragraph // model for that and see if that model applies throughout the text // (that is, "smear" it). StrongEvidenceClassify(debug_level, &rows, leftovers[i].begin, leftovers[i].end, &theory); // Pass 2b: // If we had any luck in pass 2a, we got part of the page and didn't // know how to classify a few runs of rows. Take the segments that // didn't find a model and reprocess them individually. GenericVector leftovers2; LeftoverSegments(rows, &leftovers2, leftovers[i].begin, leftovers[i].end); bool pass2a_was_useful = leftovers2.size() > 1 || (leftovers2.size() == 1 && (leftovers2[0].begin != 0 || leftovers2[0].end != rows.size())); if (pass2a_was_useful) { for (int j = 0; j < leftovers2.size(); j++) { StrongEvidenceClassify(debug_level, &rows, leftovers2[j].begin, leftovers2[j].end, &theory); } } } DebugDump(debug_level > 1, "End of Pass 2", theory, rows); // Pass 3: // These are the dregs for which we didn't have enough strong textual // and geometric clues to form matching models for. Let's see if // the geometric clues are simple enough that we could just use those. LeftoverSegments(rows, &leftovers, 0, rows.size()); for (int i = 0; i < leftovers.size(); i++) { 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); DebugDump(debug_level > 1, "End of Pass 3", theory, rows); // Pass 4: // Take everything that's still not marked up well and clear all markings. LeftoverSegments(rows, &leftovers, 0, rows.size()); for (int i = 0; i < leftovers.size(); i++) { for (int j = leftovers[i].begin; j < leftovers[i].end; j++) { rows[j].SetUnknown(); } } DebugDump(debug_level > 1, "End of Pass 4", theory, rows); // Convert all of the unique hypothesis runs to PARAs. ConvertHypothesizedModelRunsToParagraphs(debug_level, rows, row_owners, &theory); DebugDump(debug_level > 0, "Final Paragraph Segmentation", theory, rows); // Finally, clean up any dangling NULL row paragraph parents. CanonicalizeDetectionResults(row_owners, paragraphs); } // ============ Code interfacing with the rest of Tesseract ================== void InitializeTextAndBoxesPreRecognition(const MutableIterator &it, RowInfo *info) { // Set up text, lword_text, and rword_text (mostly for debug printing). STRING fake_text; PageIterator pit(static_cast(it)); bool first_word = true; if (!pit.Empty(RIL_WORD)) { do { fake_text += "x"; if (first_word) info->lword_text += "x"; info->rword_text += "x"; if (pit.IsAtFinalElement(RIL_WORD, RIL_SYMBOL) && !pit.IsAtFinalElement(RIL_TEXTLINE, RIL_SYMBOL)) { fake_text += " "; info->rword_text = ""; first_word = false; } } while (!pit.IsAtFinalElement(RIL_TEXTLINE, RIL_SYMBOL) && pit.Next(RIL_SYMBOL)); } if (fake_text.size() == 0) return; int lspaces = info->pix_ldistance / info->average_interword_space; for (int i = 0; i < lspaces; i++) { info->text += ' '; } info->text += fake_text; // Set up lword_box, rword_box, and num_words. PAGE_RES_IT page_res_it = *it.PageResIt(); WERD_RES *word_res = page_res_it.restart_row(); ROW_RES *this_row = page_res_it.row(); WERD_RES *lword = NULL; WERD_RES *rword = NULL; info->num_words = 0; do { if (word_res) { if (!lword) lword = word_res; if (rword != word_res) info->num_words++; rword = word_res; } word_res = page_res_it.forward(); } while (page_res_it.row() == this_row); if (lword) info->lword_box = lword->word->bounding_box(); if (rword) info->rword_box = rword->word->bounding_box(); } // Given a Tesseract Iterator pointing to a text line, fill in the paragraph // detector RowInfo with all relevant information from the row. void InitializeRowInfo(bool after_recognition, const MutableIterator &it, RowInfo *info) { if (it.PageResIt()->row() != NULL) { ROW *row = it.PageResIt()->row()->row; info->pix_ldistance = row->lmargin(); info->pix_rdistance = row->rmargin(); info->average_interword_space = row->space() > 0 ? row->space() : MAX(row->x_height(), 1); info->pix_xheight = row->x_height(); info->has_leaders = false; info->has_drop_cap = row->has_drop_cap(); info->ltr = true; // set below depending on word scripts } else { info->pix_ldistance = info->pix_rdistance = 0; info->average_interword_space = 1; info->pix_xheight = 1.0; info->has_leaders = false; info->has_drop_cap = false; info->ltr = true; } info->num_words = 0; info->lword_indicates_list_item = false; info->lword_likely_starts_idea = false; info->lword_likely_ends_idea = false; info->rword_indicates_list_item = false; info->rword_likely_starts_idea = false; info->rword_likely_ends_idea = false; info->has_leaders = false; info->ltr = 1; if (!after_recognition) { InitializeTextAndBoxesPreRecognition(it, info); return; } info->text = ""; char *text = it.GetUTF8Text(RIL_TEXTLINE); int trailing_ws_idx = strlen(text); // strip trailing space while (trailing_ws_idx > 0 && // isspace() only takes ASCII ((text[trailing_ws_idx - 1] & 0x80) == 0) && isspace(text[trailing_ws_idx - 1])) trailing_ws_idx--; if (trailing_ws_idx > 0) { int lspaces = info->pix_ldistance / info->average_interword_space; for (int i = 0; i < lspaces; i++) info->text += ' '; for (int i = 0; i < trailing_ws_idx; i++) info->text += text[i]; } delete []text; if (info->text.size() == 0) { return; } PAGE_RES_IT page_res_it = *it.PageResIt(); GenericVector werds; WERD_RES *word_res = page_res_it.restart_row(); ROW_RES *this_row = page_res_it.row(); int num_leaders = 0; int ltr = 0; int rtl = 0; do { if (word_res && word_res->best_choice->unichar_string().length() > 0) { werds.push_back(word_res); ltr += word_res->AnyLtrCharsInWord() ? 1 : 0; rtl += word_res->AnyRtlCharsInWord() ? 1 : 0; if (word_res->word->flag(W_REP_CHAR)) num_leaders++; } word_res = page_res_it.forward(); } while (page_res_it.row() == this_row); info->ltr = ltr >= rtl; info->has_leaders = num_leaders > 3; info->num_words = werds.size(); if (werds.size() > 0) { WERD_RES *lword = werds[0], *rword = werds[werds.size() - 1]; info->lword_text = lword->best_choice->unichar_string().string(); info->rword_text = rword->best_choice->unichar_string().string(); info->lword_box = lword->word->bounding_box(); info->rword_box = rword->word->bounding_box(); LeftWordAttributes(lword->uch_set, lword->best_choice, info->lword_text, &info->lword_indicates_list_item, &info->lword_likely_starts_idea, &info->lword_likely_ends_idea); RightWordAttributes(rword->uch_set, rword->best_choice, info->rword_text, &info->rword_indicates_list_item, &info->rword_likely_starts_idea, &info->rword_likely_ends_idea); } } // This is called after rows have been identified and words are recognized. // Much of this could be implemented before word recognition, but text helps // to identify bulleted lists and gives good signals for sentence boundaries. void DetectParagraphs(int debug_level, bool after_text_recognition, const MutableIterator *block_start, GenericVector *models) { // Clear out any preconceived notions. if (block_start->Empty(RIL_TEXTLINE)) { return; } BLOCK *block = block_start->PageResIt()->block()->block; block->para_list()->clear(); bool is_image_block = block->poly_block() && !block->poly_block()->IsText(); // Convert the Tesseract structures to RowInfos // for the paragraph detection algorithm. MutableIterator row(*block_start); if (row.Empty(RIL_TEXTLINE)) return; // end of input already. GenericVector row_infos; do { if (!row.PageResIt()->row()) continue; // empty row. row.PageResIt()->row()->row->set_para(NULL); row_infos.push_back(RowInfo()); RowInfo &ri = row_infos.back(); InitializeRowInfo(after_text_recognition, row, &ri); } while (!row.IsAtFinalElement(RIL_BLOCK, RIL_TEXTLINE) && row.Next(RIL_TEXTLINE)); // If we're called before text recognition, we might not have // tight block bounding boxes, so trim by the minimum on each side. if (row_infos.size() > 0) { int min_lmargin = row_infos[0].pix_ldistance; int min_rmargin = row_infos[0].pix_rdistance; for (int i = 1; i < row_infos.size(); i++) { if (row_infos[i].pix_ldistance < min_lmargin) min_lmargin = row_infos[i].pix_ldistance; if (row_infos[i].pix_rdistance < min_rmargin) min_rmargin = row_infos[i].pix_rdistance; } if (min_lmargin > 0 || min_rmargin > 0) { for (int i = 0; i < row_infos.size(); i++) { row_infos[i].pix_ldistance -= min_lmargin; row_infos[i].pix_rdistance -= min_rmargin; } } } // Run the paragraph detection algorithm. GenericVector row_owners; GenericVector the_paragraphs; if (!is_image_block) { DetectParagraphs(debug_level, &row_infos, &row_owners, block->para_list(), models); } else { row_owners.init_to_size(row_infos.size(), NULL); CanonicalizeDetectionResults(&row_owners, block->para_list()); } // Now stitch in the row_owners into the rows. row = *block_start; for (int i = 0; i < row_owners.size(); i++) { while (!row.PageResIt()->row()) row.Next(RIL_TEXTLINE); row.PageResIt()->row()->row->set_para(row_owners[i]); row.Next(RIL_TEXTLINE); } } } // namespace tesseract-3.04.01/ccmain/paragraphs.h000066400000000000000000000105501266071204500174130ustar00rootroot00000000000000/********************************************************************** * File: paragraphs.h * Description: Paragraph Detection data structures. * Author: David Eger * Created: 25 February 2011 * * (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_CCMAIN_PARAGRAPHS_H_ #define TESSERACT_CCMAIN_PARAGRAPHS_H_ #include "rect.h" #include "ocrpara.h" #include "genericvector.h" #include "strngs.h" class WERD; class UNICHARSET; namespace tesseract { class MutableIterator; // This structure captures all information needed about a text line for the // purposes of paragraph detection. It is meant to be exceedingly light-weight // so that we can easily test paragraph detection independent of the rest of // Tesseract. class RowInfo { public: // Constant data derived from Tesseract output. STRING text; // the full UTF-8 text of the line. bool ltr; // whether the majority of the text is left-to-right // TODO(eger) make this more fine-grained. bool has_leaders; // does the line contain leader dots (.....)? bool has_drop_cap; // does the line have a drop cap? int pix_ldistance; // distance to the left pblock boundary in pixels int pix_rdistance; // distance to the right pblock boundary in pixels float pix_xheight; // guessed xheight for the line int average_interword_space; // average space between words in pixels. int num_words; TBOX lword_box; // in normalized (horiz text rows) space TBOX rword_box; // in normalized (horiz text rows) space STRING lword_text; // the UTF-8 text of the leftmost werd STRING rword_text; // the UTF-8 text of the rightmost werd // The text of a paragraph typically starts with the start of an idea and // ends with the end of an idea. Here we define paragraph as something that // may have a first line indent and a body indent which may be different. // Typical words that start an idea are: // 1. Words in western scripts that start with // a capital letter, for example "The" // 2. Bulleted or numbered list items, for // example "2." // Typical words which end an idea are words ending in punctuation marks. In // this vocabulary, each list item is represented as a paragraph. bool lword_indicates_list_item; bool lword_likely_starts_idea; bool lword_likely_ends_idea; bool rword_indicates_list_item; bool rword_likely_starts_idea; bool rword_likely_ends_idea; }; // Main entry point for Paragraph Detection Algorithm. // // Given a set of equally spaced textlines (described by row_infos), // Split them into paragraphs. See http://goto/paragraphstalk // // Output: // row_owners - one pointer for each row, to the paragraph it belongs to. // paragraphs - this is the actual list of PARA objects. // models - the list of paragraph models referenced by the PARA objects. // caller is responsible for deleting the models. void DetectParagraphs(int debug_level, GenericVector *row_infos, GenericVector *row_owners, PARA_LIST *paragraphs, GenericVector *models); // Given a MutableIterator to the start of a block, run DetectParagraphs on // that block and commit the results to the underlying ROW and BLOCK structs, // saving the ParagraphModels in models. Caller owns the models. // We use unicharset during the function to answer questions such as "is the // first letter of this word upper case?" void DetectParagraphs(int debug_level, bool after_text_recognition, const MutableIterator *block_start, GenericVector *models); } // namespace #endif // TESSERACT_CCMAIN_PARAGRAPHS_H_ tesseract-3.04.01/ccmain/paragraphs_internal.h000066400000000000000000000306431266071204500213140ustar00rootroot00000000000000/********************************************************************** * File: paragraphs.h * Description: Paragraph Detection internal data structures. * Author: David Eger * Created: 11 March 2011 * * (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_CCMAIN_PARAGRAPHS_INTERNAL_H_ #define TESSERACT_CCMAIN_PARAGRAPHS_INTERNAL_H_ #include "paragraphs.h" #ifdef _MSC_VER #include #else #include "strings.h" #endif // NO CODE OUTSIDE OF paragraphs.cpp AND TESTS SHOULD NEED TO ACCESS // DATA STRUCTURES OR FUNCTIONS IN THIS FILE. class WERD_CHOICE; namespace tesseract { // Return whether the given word is likely to be a list item start word. bool AsciiLikelyListItem(const STRING &word); // Return the first Unicode Codepoint from werd[pos]. int UnicodeFor(const UNICHARSET *u, const WERD_CHOICE *werd, int pos); // Set right word attributes given either a unicharset and werd or a utf8 // string. void RightWordAttributes(const UNICHARSET *unicharset, const WERD_CHOICE *werd, const STRING &utf8, bool *is_list, bool *starts_idea, bool *ends_idea); // Set left word attributes given either a unicharset and werd or a utf8 string. void LeftWordAttributes(const UNICHARSET *unicharset, const WERD_CHOICE *werd, const STRING &utf8, bool *is_list, bool *starts_idea, bool *ends_idea); enum LineType { LT_START = 'S', // First line of a paragraph. LT_BODY = 'C', // Continuation line of a paragraph. LT_UNKNOWN = 'U', // No clues. LT_MULTIPLE = 'M', // Matches for both LT_START and LT_BODY. }; // The first paragraph in a page of body text is often un-indented. // This is a typographic convention which is common to indicate either that: // (1) The paragraph is the continuation of a previous paragraph, or // (2) The paragraph is the first paragraph in a chapter. // // I refer to such paragraphs as "crown"s, and the output of the paragraph // detection algorithm attempts to give them the same paragraph model as // the rest of the body text. // // Nonetheless, while building hypotheses, it is useful to mark the lines // of crown paragraphs temporarily as crowns, either aligned left or right. extern const ParagraphModel *kCrownLeft; extern const ParagraphModel *kCrownRight; inline bool StrongModel(const ParagraphModel *model) { return model != NULL && model != kCrownLeft && model != kCrownRight; } struct LineHypothesis { LineHypothesis() : ty(LT_UNKNOWN), model(NULL) {} LineHypothesis(LineType line_type, const ParagraphModel *m) : ty(line_type), model(m) {} LineHypothesis(const LineHypothesis &other) : ty(other.ty), model(other.model) {} bool operator==(const LineHypothesis &other) const { return ty == other.ty && model == other.model; } LineType ty; const ParagraphModel *model; }; class ParagraphTheory; // Forward Declaration typedef GenericVectorEqEq SetOfModels; // Row Scratch Registers are data generated by the paragraph detection // algorithm based on a RowInfo input. class RowScratchRegisters { public: // We presume row will outlive us. void Init(const RowInfo &row); LineType GetLineType() const; LineType GetLineType(const ParagraphModel *model) const; // Mark this as a start line type, sans model. This is useful for the // initial marking of probable body lines or paragraph start lines. void SetStartLine(); // Mark this as a body line type, sans model. This is useful for the // initial marking of probably body lines or paragraph start lines. void SetBodyLine(); // Record that this row fits as a paragraph start line in the given model, void AddStartLine(const ParagraphModel *model); // Record that this row fits as a paragraph body line in the given model, void AddBodyLine(const ParagraphModel *model); // Clear all hypotheses about this line. void SetUnknown() { hypotheses_.truncate(0); } // Append all hypotheses of strong models that match this row as a start. void StartHypotheses(SetOfModels *models) const; // Append all hypotheses of strong models matching this row. void StrongHypotheses(SetOfModels *models) const; // Append all hypotheses for this row. void NonNullHypotheses(SetOfModels *models) const; // Discard any hypotheses whose model is not in the given list. void DiscardNonMatchingHypotheses(const SetOfModels &models); // If we have only one hypothesis and that is that this line is a paragraph // start line of a certain model, return that model. Else return NULL. const ParagraphModel *UniqueStartHypothesis() const; // If we have only one hypothesis and that is that this line is a paragraph // body line of a certain model, return that model. Else return NULL. const ParagraphModel *UniqueBodyHypothesis() const; // Return the indentation for the side opposite of the aligned side. int OffsideIndent(tesseract::ParagraphJustification just) const { switch (just) { case tesseract::JUSTIFICATION_RIGHT: return lindent_; case tesseract::JUSTIFICATION_LEFT: return rindent_; default: return lindent_ > rindent_ ? lindent_ : rindent_; } } // Return the indentation for the side the text is aligned to. int AlignsideIndent(tesseract::ParagraphJustification just) const { switch (just) { case tesseract::JUSTIFICATION_RIGHT: return rindent_; case tesseract::JUSTIFICATION_LEFT: return lindent_; default: return lindent_ > rindent_ ? lindent_ : rindent_; } } // Append header fields to a vector of row headings. static void AppendDebugHeaderFields(GenericVector *header); // Append data for this row to a vector of debug strings. void AppendDebugInfo(const ParagraphTheory &theory, GenericVector *dbg) const; const RowInfo *ri_; // These four constants form a horizontal box model for the white space // on the edges of each line. At each point in the algorithm, the following // shall hold: // ri_->pix_ldistance = lmargin_ + lindent_ // ri_->pix_rdistance = rindent_ + rmargin_ int lmargin_; int lindent_; int rindent_; int rmargin_; private: // Hypotheses of either LT_START or LT_BODY GenericVectorEqEq hypotheses_; }; // A collection of convenience functions for wrapping the set of // Paragraph Models we believe correctly model the paragraphs in the image. class ParagraphTheory { public: // We presume models will outlive us, and that models will take ownership // of any ParagraphModel *'s we add. explicit ParagraphTheory(GenericVector *models) : models_(models) {} GenericVector &models() { return *models_; } const GenericVector &models() const { return *models_; } // Return an existing model if one that is Comparable() can be found. // Else, allocate a new copy of model to save and return a pointer to it. const ParagraphModel *AddModel(const ParagraphModel &model); // Discard any models we've made that are not in the list of used models. void DiscardUnusedModels(const SetOfModels &used_models); // Return the set of all non-centered models. void NonCenteredModels(SetOfModels *models); // If any of the non-centered paragraph models we know about fit // rows[start, end), return it. Else NULL. const ParagraphModel *Fits(const GenericVector *rows, int start, int end) const; int IndexOf(const ParagraphModel *model) const; private: GenericVector *models_; GenericVectorEqEq models_we_added_; }; bool ValidFirstLine(const GenericVector *rows, int row, const ParagraphModel *model); bool ValidBodyLine(const GenericVector *rows, int row, const ParagraphModel *model); bool CrownCompatible(const GenericVector *rows, int a, int b, const ParagraphModel *model); // A class for smearing Paragraph Model hypotheses to surrounding rows. // The idea here is that StrongEvidenceClassify first marks only exceedingly // obvious start and body rows and constructs models of them. Thereafter, // we may have left over unmarked lines (mostly end-of-paragraph lines) which // were too short to have much confidence about, but which fit the models we've // constructed perfectly and which we ought to mark. This class is used to // "smear" our models over the text. class ParagraphModelSmearer { public: ParagraphModelSmearer(GenericVector *rows, int row_start, int row_end, ParagraphTheory *theory); // Smear forward paragraph models from existing row markings to subsequent // text lines if they fit, and mark any thereafter still unmodeled rows // with any model in the theory that fits them. void Smear(); private: // Record in open_models_ for rows [start_row, end_row) the list of models // currently open at each row. // A model is still open in a row if some previous row has said model as a // start hypothesis, and all rows since (including this row) would fit as // either a body or start line in that model. void CalculateOpenModels(int row_start, int row_end); SetOfModels &OpenModels(int row) { return open_models_[row - row_start_ + 1]; } ParagraphTheory *theory_; GenericVector *rows_; int row_start_; int row_end_; // open_models_ corresponds to rows[start_row_ - 1, end_row_] // // open_models_: Contains models which there was an active (open) paragraph // as of the previous line and for which the left and right // indents admit the possibility that this text line continues // to fit the same model. // TODO(eger): Think about whether we can get rid of "Open" models and just // use the current hypotheses on RowScratchRegisters. GenericVector open_models_; }; // Clear all hypotheses about lines [start, end) and reset the margins to the // percentile (0..100) value of the left and right row edges for this run of // rows. void RecomputeMarginsAndClearHypotheses( GenericVector *rows, int start, int end, int percentile); // Return the median inter-word space in rows[row_start, row_end). int InterwordSpace(const GenericVector &rows, int row_start, int row_end); // Return whether the first word on the after line can fit in the space at // the end of the before line (knowing which way the text is aligned and read). bool FirstWordWouldHaveFit(const RowScratchRegisters &before, const RowScratchRegisters &after, tesseract::ParagraphJustification justification); // Return whether the first word on the after line can fit in the space at // the end of the before line (not knowing the text alignment). bool FirstWordWouldHaveFit(const RowScratchRegisters &before, const RowScratchRegisters &after); // Do rows[start, end) form a single instance of the given paragraph model? bool RowsFitModel(const GenericVector *rows, int start, int end, const ParagraphModel *model); // Do the text and geometry of two rows support a paragraph break between them? bool LikelyParagraphStart(const RowScratchRegisters &before, const RowScratchRegisters &after, tesseract::ParagraphJustification j); // Given a set of row_owners pointing to PARAs or NULL (no paragraph known), // normalize each row_owner to point to an actual PARA, and output the // paragraphs in order onto paragraphs. void CanonicalizeDetectionResults( GenericVector *row_owners, PARA_LIST *paragraphs); } // namespace #endif // TESSERACT_CCMAIN_PARAGRAPHS_INTERNAL_H_ tesseract-3.04.01/ccmain/paramsd.cpp000066400000000000000000000263651266071204500172600ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: paramsd.cpp // Description: Tesseract parameter Editor // Author: Joern Wanke // Created: Wed Jul 18 10:05:01 PDT 2007 // // (C) Copyright 2007, 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. // /////////////////////////////////////////////////////////////////////// // // The parameters editor is used to edit all the parameters used within // tesseract from the ui. #ifdef _WIN32 #else #include #include #endif #include // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #ifndef GRAPHICS_DISABLED #include "paramsd.h" #include "params.h" #include "scrollview.h" #include "svmnode.h" #define VARDIR "configs/" /*parameters files */ #define MAX_ITEMS_IN_SUBMENU 30 // The following variables should remain static globals, since they // are used by debug editor, which uses a single Tesseract instance. // // Contains the mappings from unique VC ids to their actual pointers. static std::map vcMap; static int nrParams = 0; static int writeCommands[2]; ELISTIZE(ParamContent) // Constructors for the various ParamTypes. ParamContent::ParamContent(tesseract::StringParam* it) { my_id_ = nrParams; nrParams++; param_type_ = VT_STRING; sIt = it; vcMap[my_id_] = this; } // Constructors for the various ParamTypes. ParamContent::ParamContent(tesseract::IntParam* it) { my_id_ = nrParams; nrParams++; param_type_ = VT_INTEGER; iIt = it; vcMap[my_id_] = this; } // Constructors for the various ParamTypes. ParamContent::ParamContent(tesseract::BoolParam* it) { my_id_ = nrParams; nrParams++; param_type_ = VT_BOOLEAN; bIt = it; vcMap[my_id_] = this; } // Constructors for the various ParamTypes. ParamContent::ParamContent(tesseract::DoubleParam* it) { my_id_ = nrParams; nrParams++; param_type_ = VT_DOUBLE; dIt = it; vcMap[my_id_] = this; } // Gets a VC object identified by its ID. ParamContent* ParamContent::GetParamContentById(int id) { return vcMap[id]; } // Copy the first N words from the source string to the target string. // Words are delimited by "_". void ParamsEditor::GetFirstWords( const char *s, // source string int n, // number of words char *t // target string ) { int full_length = strlen(s); int reqd_len = 0; // No. of chars requird const char *next_word = s; while ((n > 0) && reqd_len < full_length) { reqd_len += strcspn(next_word, "_") + 1; next_word += reqd_len; n--; } strncpy(t, s, reqd_len); t[reqd_len] = '\0'; // ensure null terminal } // Getter for the name. const char* ParamContent::GetName() const { if (param_type_ == VT_INTEGER) { return iIt->name_str(); } else if (param_type_ == VT_BOOLEAN) { return bIt->name_str(); } else if (param_type_ == VT_DOUBLE) { return dIt->name_str(); } else if (param_type_ == VT_STRING) { return sIt->name_str(); } else return "ERROR: ParamContent::GetName()"; } // Getter for the description. const char* ParamContent::GetDescription() const { if (param_type_ == VT_INTEGER) { return iIt->info_str(); } else if (param_type_ == VT_BOOLEAN) { return bIt->info_str(); } else if (param_type_ == VT_DOUBLE) { return dIt->info_str(); } else if (param_type_ == VT_STRING) { return sIt->info_str(); } else return NULL; } // Getter for the value. STRING ParamContent::GetValue() const { STRING result; if (param_type_ == VT_INTEGER) { result.add_str_int("", *iIt); } else if (param_type_ == VT_BOOLEAN) { result.add_str_int("", *bIt); } else if (param_type_ == VT_DOUBLE) { result.add_str_double("", *dIt); } else if (param_type_ == VT_STRING) { if (((STRING) * (sIt)).string() != NULL) { result = sIt->string(); } else { result = "Null"; } } return result; } // Setter for the value. void ParamContent::SetValue(const char* val) { // TODO (wanke) Test if the values actually are properly converted. // (Quickly visible impacts?) changed_ = TRUE; if (param_type_ == VT_INTEGER) { iIt->set_value(atoi(val)); } else if (param_type_ == VT_BOOLEAN) { bIt->set_value(atoi(val)); } else if (param_type_ == VT_DOUBLE) { dIt->set_value(strtod(val, NULL)); } else if (param_type_ == VT_STRING) { sIt->set_value(val); } } // Gets the up to the first 3 prefixes from s (split by _). // For example, tesseract_foo_bar will be split into tesseract,foo and bar. void ParamsEditor::GetPrefixes(const char* s, STRING* level_one, STRING* level_two, STRING* level_three) { char* p = new char[1024]; GetFirstWords(s, 1, p); *level_one = p; GetFirstWords(s, 2, p); *level_two = p; GetFirstWords(s, 3, p); *level_three = p; delete[] p; } // Compare two VC objects by their name. int ParamContent::Compare(const void* v1, const void* v2) { const ParamContent* one = *reinterpret_cast(v1); const ParamContent* two = *reinterpret_cast(v2); return strcmp(one->GetName(), two->GetName()); } // Find all editable parameters used within tesseract and create a // SVMenuNode tree from it. // TODO (wanke): This is actually sort of hackish. SVMenuNode* ParamsEditor::BuildListOfAllLeaves(tesseract::Tesseract *tess) { SVMenuNode* mr = new SVMenuNode(); ParamContent_LIST vclist; ParamContent_IT vc_it(&vclist); // Amount counts the number of entries for a specific char*. // TODO(rays) get rid of the use of std::map. std::map amount; // Add all parameters to a list. int v, i; int num_iterations = (tess->params() == NULL) ? 1 : 2; for (v = 0; v < num_iterations; ++v) { tesseract::ParamsVectors *vec = (v == 0) ? GlobalParams() : tess->params(); for (i = 0; i < vec->int_params.size(); ++i) { vc_it.add_after_then_move(new ParamContent(vec->int_params[i])); } for (i = 0; i < vec->bool_params.size(); ++i) { vc_it.add_after_then_move(new ParamContent(vec->bool_params[i])); } for (i = 0; i < vec->string_params.size(); ++i) { vc_it.add_after_then_move(new ParamContent(vec->string_params[i])); } for (i = 0; i < vec->double_params.size(); ++i) { vc_it.add_after_then_move(new ParamContent(vec->double_params[i])); } } // Count the # of entries starting with a specific prefix. for (vc_it.mark_cycle_pt(); !vc_it.cycled_list(); vc_it.forward()) { ParamContent* vc = vc_it.data(); STRING tag; STRING tag2; STRING tag3; GetPrefixes(vc->GetName(), &tag, &tag2, &tag3); amount[tag.string()]++; amount[tag2.string()]++; amount[tag3.string()]++; } vclist.sort(ParamContent::Compare); // Sort the list alphabetically. SVMenuNode* other = mr->AddChild("OTHER"); // go through the list again and this time create the menu structure. vc_it.move_to_first(); for (vc_it.mark_cycle_pt(); !vc_it.cycled_list(); vc_it.forward()) { ParamContent* vc = vc_it.data(); STRING tag; STRING tag2; STRING tag3; GetPrefixes(vc->GetName(), &tag, &tag2, &tag3); if (amount[tag.string()] == 1) { other->AddChild(vc->GetName(), vc->GetId(), vc->GetValue().string(), vc->GetDescription()); } else { // More than one would use this submenu -> create submenu. SVMenuNode* sv = mr->AddChild(tag.string()); if ((amount[tag.string()] <= MAX_ITEMS_IN_SUBMENU) || (amount[tag2.string()] <= 1)) { sv->AddChild(vc->GetName(), vc->GetId(), vc->GetValue().string(), vc->GetDescription()); } else { // Make subsubmenus. SVMenuNode* sv2 = sv->AddChild(tag2.string()); sv2->AddChild(vc->GetName(), vc->GetId(), vc->GetValue().string(), vc->GetDescription()); } } } return mr; } // Event listener. Waits for SVET_POPUP events and processes them. void ParamsEditor::Notify(const SVEvent* sve) { if (sve->type == SVET_POPUP) { // only catch SVET_POPUP! char* param = sve->parameter; if (sve->command_id == writeCommands[0]) { WriteParams(param, false); } else if (sve->command_id == writeCommands[1]) { WriteParams(param, true); } else { ParamContent* vc = ParamContent::GetParamContentById( sve->command_id); vc->SetValue(param); sv_window_->AddMessage("Setting %s to %s", vc->GetName(), vc->GetValue().string()); } } } // Integrate the parameters editor as popupmenu into the existing scrollview // window (usually the pg editor). If sv == null, create a new empty // empty window and attach the parameters editor to that window (ugly). ParamsEditor::ParamsEditor(tesseract::Tesseract* tess, ScrollView* sv) { if (sv == NULL) { const char* name = "ParamEditorMAIN"; sv = new ScrollView(name, 1, 1, 200, 200, 300, 200); } sv_window_ = sv; //Only one event handler per window. //sv->AddEventHandler((SVEventHandler*) this); SVMenuNode* svMenuRoot = BuildListOfAllLeaves(tess); STRING paramfile; paramfile = tess->datadir; paramfile += VARDIR; // parameters dir paramfile += "edited"; // actual name SVMenuNode* std_menu = svMenuRoot->AddChild ("Build Config File"); writeCommands[0] = nrParams+1; std_menu->AddChild("All Parameters", writeCommands[0], paramfile.string(), "Config file name?"); writeCommands[1] = nrParams+2; std_menu->AddChild ("changed_ Parameters Only", writeCommands[1], paramfile.string(), "Config file name?"); svMenuRoot->BuildMenu(sv, false); } // Write all (changed_) parameters to a config file. void ParamsEditor::WriteParams(char *filename, bool changes_only) { FILE *fp; // input file char msg_str[255]; // if file exists if ((fp = fopen (filename, "rb")) != NULL) { fclose(fp); sprintf (msg_str, "Overwrite file " "%s" "? (Y/N)", filename); int a = sv_window_->ShowYesNoDialog(msg_str); if (a == 'n') { return; } // don't write } fp = fopen (filename, "wb"); // can we write to it? if (fp == NULL) { sv_window_->AddMessage("Can't write to file " "%s" "", filename); return; } for (std::map::iterator iter = vcMap.begin(); iter != vcMap.end(); ++iter) { ParamContent* cur = iter->second; if (!changes_only || cur->HasChanged()) { fprintf(fp, "%-25s %-12s # %s\n", cur->GetName(), cur->GetValue().string(), cur->GetDescription()); } } fclose(fp); } #endif tesseract-3.04.01/ccmain/paramsd.h000066400000000000000000000103041266071204500167070ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: paramsd.cpp // Description: Tesseract parameter editor // Author: Joern Wanke // Created: Wed Jul 18 10:05:01 PDT 2007 // // (C) Copyright 2007, 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. // /////////////////////////////////////////////////////////////////////// // // Tesseract parameter editor is used to edit all the parameters used // within tesseract from the ui. #ifndef GRAPHICS_DISABLED #ifndef VARABLED_H #define VARABLED_H #include "elst.h" #ifndef NO_CUBE_BUILD #include "scrollview.h" #endif #include "params.h" #include "tesseractclass.h" class SVMenuNode; // A list of all possible parameter types used. enum ParamType { VT_INTEGER, VT_BOOLEAN, VT_STRING, VT_DOUBLE }; // A rather hackish helper structure which can take any kind of parameter input // (defined by ParamType) and do a couple of common operations on them, like // comparisond or getting its value. It is used in the context of the // ParamsEditor as a bridge from the internal tesseract parameters to the // ones displayed by the ScrollView server. class ParamContent : public ELIST_LINK { public: // Compare two VC objects by their name. static int Compare(const void* v1, const void* v2); // Gets a VC object identified by its ID. static ParamContent* GetParamContentById(int id); // Constructors for the various ParamTypes. ParamContent() { } explicit ParamContent(tesseract::StringParam* it); explicit ParamContent(tesseract::IntParam* it); explicit ParamContent(tesseract::BoolParam* it); explicit ParamContent(tesseract::DoubleParam* it); // Getters and Setters. void SetValue(const char* val); STRING GetValue() const; const char* GetName() const; const char* GetDescription() const; int GetId() { return my_id_; } bool HasChanged() { return changed_; } private: // The unique ID of this VC object. int my_id_; // Whether the parameter was changed_ and thus needs to be rewritten. bool changed_; // The actual ParamType of this VC object. ParamType param_type_; tesseract::StringParam* sIt; tesseract::IntParam* iIt; tesseract::BoolParam* bIt; tesseract::DoubleParam* dIt; }; ELISTIZEH(ParamContent) // The parameters editor enables the user to edit all the parameters used within // tesseract. It can be invoked on its own, but is supposed to be invoked by // the program editor. class ParamsEditor : public SVEventHandler { public: // Integrate the parameters editor as popupmenu into the existing scrollview // window (usually the pg editor). If sv == null, create a new empty // empty window and attach the parameter editor to that window (ugly). explicit ParamsEditor(tesseract::Tesseract*, ScrollView* sv = NULL); // Event listener. Waits for SVET_POPUP events and processes them. void Notify(const SVEvent* sve); private: // Gets the up to the first 3 prefixes from s (split by _). // For example, tesseract_foo_bar will be split into tesseract,foo and bar. void GetPrefixes(const char* s, STRING* level_one, STRING* level_two, STRING* level_three); // Gets the first n words (split by _) and puts them in t. // For example, tesseract_foo_bar with N=2 will yield tesseract_foo_. void GetFirstWords(const char *s, // source string int n, // number of words char *t); // target string // Find all editable parameters used within tesseract and create a // SVMenuNode tree from it. SVMenuNode *BuildListOfAllLeaves(tesseract::Tesseract *tess); // Write all (changed_) parameters to a config file. void WriteParams(char* filename, bool changes_only); ScrollView* sv_window_; }; #endif #endif tesseract-3.04.01/ccmain/pgedit.cpp000066400000000000000000000771751266071204500171120ustar00rootroot00000000000000/********************************************************************** * File: pgedit.cpp (Formerly pgeditor.c) * Description: Page structure file editor * Author: Phil Cheatle * Created: Thu Oct 10 16:25:24 BST 1991 * *(C) Copyright 1991, 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. * **********************************************************************/ #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #endif // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "pgedit.h" #include #include #include "blread.h" #include "control.h" #include "paramsd.h" #include "pageres.h" #include "tordmain.h" #include "scrollview.h" #include "svmnode.h" #include "statistc.h" #include "tesseractclass.h" #include "werdit.h" #ifndef GRAPHICS_DISABLED #define ASC_HEIGHT (2 * kBlnBaselineOffset + kBlnXHeight) #define X_HEIGHT (kBlnBaselineOffset + kBlnXHeight) #define BL_HEIGHT kBlnBaselineOffset #define DESC_HEIGHT 0 #define MAXSPACING 128 /*max expected spacing in pix */ const ERRCODE EMPTYBLOCKLIST = "No blocks to edit"; enum CMD_EVENTS { NULL_CMD_EVENT, CHANGE_DISP_CMD_EVENT, DUMP_WERD_CMD_EVENT, SHOW_POINT_CMD_EVENT, SHOW_BLN_WERD_CMD_EVENT, DEBUG_WERD_CMD_EVENT, BLAMER_CMD_EVENT, BOUNDING_BOX_CMD_EVENT, CORRECT_TEXT_CMD_EVENT, POLYGONAL_CMD_EVENT, BL_NORM_CMD_EVENT, BITMAP_CMD_EVENT, IMAGE_CMD_EVENT, BLOCKS_CMD_EVENT, BASELINES_CMD_EVENT, UNIFORM_DISP_CMD_EVENT, REFRESH_CMD_EVENT, QUIT_CMD_EVENT, RECOG_WERDS, RECOG_PSEUDO, SHOW_BLOB_FEATURES, SHOW_SUBSCRIPT_CMD_EVENT, SHOW_SUPERSCRIPT_CMD_EVENT, SHOW_ITALIC_CMD_EVENT, SHOW_BOLD_CMD_EVENT, SHOW_UNDERLINE_CMD_EVENT, SHOW_FIXEDPITCH_CMD_EVENT, SHOW_SERIF_CMD_EVENT, SHOW_SMALLCAPS_CMD_EVENT, SHOW_DROPCAPS_CMD_EVENT, }; enum ColorationMode { CM_RAINBOW, CM_SUBSCRIPT, CM_SUPERSCRIPT, CM_ITALIC, CM_BOLD, CM_UNDERLINE, CM_FIXEDPITCH, CM_SERIF, CM_SMALLCAPS, CM_DROPCAPS }; /* * * Some global data * */ ScrollView* image_win; ParamsEditor* pe; bool stillRunning = false; #ifdef __UNIX__ FILE *debug_window = NULL; // opened on demand #endif ScrollView* bln_word_window = NULL; // baseline norm words CMD_EVENTS mode = CHANGE_DISP_CMD_EVENT; // selected words op bool recog_done = false; // recog_all_words was called // These variables should remain global, since they are only used for the // debug mode (in which only a single Tesseract thread/instance will be exist). BITS16 word_display_mode; static ColorationMode color_mode = CM_RAINBOW; BOOL8 display_image = FALSE; BOOL8 display_blocks = FALSE; BOOL8 display_baselines = FALSE; PAGE_RES *current_page_res = NULL; STRING_VAR(editor_image_win_name, "EditorImage", "Editor image window name"); INT_VAR(editor_image_xpos, 590, "Editor image X Pos"); INT_VAR(editor_image_ypos, 10, "Editor image Y Pos"); INT_VAR(editor_image_menuheight, 50, "Add to image height for menu bar"); INT_VAR(editor_image_word_bb_color, ScrollView::BLUE, "Word bounding box colour"); INT_VAR(editor_image_blob_bb_color, ScrollView::YELLOW, "Blob bounding box colour"); INT_VAR(editor_image_text_color, ScrollView::WHITE, "Correct text colour"); STRING_VAR(editor_dbwin_name, "EditorDBWin", "Editor debug window name"); INT_VAR(editor_dbwin_xpos, 50, "Editor debug window X Pos"); INT_VAR(editor_dbwin_ypos, 500, "Editor debug window Y Pos"); INT_VAR(editor_dbwin_height, 24, "Editor debug window height"); INT_VAR(editor_dbwin_width, 80, "Editor debug window width"); STRING_VAR(editor_word_name, "BlnWords", "BL normalized word window"); INT_VAR(editor_word_xpos, 60, "Word window X Pos"); INT_VAR(editor_word_ypos, 510, "Word window Y Pos"); INT_VAR(editor_word_height, 240, "Word window height"); INT_VAR(editor_word_width, 655, "Word window width"); STRING_VAR(editor_debug_config_file, "", "Config file to apply to single words"); class BlnEventHandler : public SVEventHandler { public: void Notify(const SVEvent* sv_event) { if (sv_event->type == SVET_DESTROY) bln_word_window = NULL; else if (sv_event->type == SVET_CLICK) show_point(current_page_res, sv_event->x, sv_event->y); } }; /** * bln_word_window_handle() * * @return a WINDOW for the word window, creating it if necessary */ ScrollView* bln_word_window_handle() { // return handle // not opened yet if (bln_word_window == NULL) { pgeditor_msg("Creating BLN word window..."); bln_word_window = new ScrollView(editor_word_name.string(), editor_word_xpos, editor_word_ypos, editor_word_width, editor_word_height, 4000, 4000, true); BlnEventHandler* a = new BlnEventHandler(); bln_word_window->AddEventHandler(a); pgeditor_msg("Creating BLN word window...Done"); } return bln_word_window; } /** * build_image_window() * * Destroy the existing image window if there is one. Work out how big the * new window needs to be. Create it and re-display. */ void build_image_window(int width, int height) { if (image_win != NULL) { delete image_win; } image_win = new ScrollView(editor_image_win_name.string(), editor_image_xpos, editor_image_ypos, width + 1, height + editor_image_menuheight + 1, width, height, true); } /** * display_bln_lines() * * Display normalized baseline, x-height, ascender limit and descender limit */ void display_bln_lines(ScrollView* window, ScrollView::Color colour, float scale_factor, float y_offset, float minx, float maxx) { window->Pen(colour); window->Line(minx, y_offset + scale_factor * DESC_HEIGHT, maxx, y_offset + scale_factor * DESC_HEIGHT); window->Line(minx, y_offset + scale_factor * BL_HEIGHT, maxx, y_offset + scale_factor * BL_HEIGHT); window->Line(minx, y_offset + scale_factor * X_HEIGHT, maxx, y_offset + scale_factor * X_HEIGHT); window->Line(minx, y_offset + scale_factor * ASC_HEIGHT, maxx, y_offset + scale_factor * ASC_HEIGHT); } /** * notify() * * Event handler that processes incoming events, either forwarding * them to process_cmd_win_event or process_image_event. * */ void PGEventHandler::Notify(const SVEvent* event) { char myval = '0'; if (event->type == SVET_POPUP) { pe->Notify(event); } // These are handled by ParamsEditor else if (event->type == SVET_EXIT) { stillRunning = false; } else if (event->type == SVET_MENU) { if (strcmp(event->parameter, "true") == 0) { myval = 'T'; } else if (strcmp(event->parameter, "false") == 0) { myval = 'F'; } tess_->process_cmd_win_event(event->command_id, &myval); } else { tess_->process_image_event(*event); } } /** * build_menu() * * Construct the menu tree used by the command window */ namespace tesseract { SVMenuNode *Tesseract::build_menu_new() { SVMenuNode* parent_menu; SVMenuNode* root_menu_item = new SVMenuNode(); SVMenuNode* modes_menu_item = root_menu_item->AddChild("MODES"); modes_menu_item->AddChild("Change Display", CHANGE_DISP_CMD_EVENT); modes_menu_item->AddChild("Dump Word", DUMP_WERD_CMD_EVENT); modes_menu_item->AddChild("Show Point", SHOW_POINT_CMD_EVENT); modes_menu_item->AddChild("Show BL Norm Word", SHOW_BLN_WERD_CMD_EVENT); modes_menu_item->AddChild("Config Words", DEBUG_WERD_CMD_EVENT); modes_menu_item->AddChild("Recog Words", RECOG_WERDS); modes_menu_item->AddChild("Recog Blobs", RECOG_PSEUDO); modes_menu_item->AddChild("Show Blob Features", SHOW_BLOB_FEATURES); parent_menu = root_menu_item->AddChild("DISPLAY"); parent_menu->AddChild("Blamer", BLAMER_CMD_EVENT, FALSE); parent_menu->AddChild("Bounding Boxes", BOUNDING_BOX_CMD_EVENT, FALSE); parent_menu->AddChild("Correct Text", CORRECT_TEXT_CMD_EVENT, FALSE); parent_menu->AddChild("Polygonal Approx", POLYGONAL_CMD_EVENT, FALSE); parent_menu->AddChild("Baseline Normalized", BL_NORM_CMD_EVENT, FALSE); parent_menu->AddChild("Edge Steps", BITMAP_CMD_EVENT, TRUE); parent_menu->AddChild("Subscripts", SHOW_SUBSCRIPT_CMD_EVENT); parent_menu->AddChild("Superscripts", SHOW_SUPERSCRIPT_CMD_EVENT); parent_menu->AddChild("Italics", SHOW_ITALIC_CMD_EVENT); parent_menu->AddChild("Bold", SHOW_BOLD_CMD_EVENT); parent_menu->AddChild("Underline", SHOW_UNDERLINE_CMD_EVENT); parent_menu->AddChild("FixedPitch", SHOW_FIXEDPITCH_CMD_EVENT); parent_menu->AddChild("Serifs", SHOW_SERIF_CMD_EVENT); parent_menu->AddChild("SmallCaps", SHOW_SMALLCAPS_CMD_EVENT); parent_menu->AddChild("DropCaps", SHOW_DROPCAPS_CMD_EVENT); parent_menu = root_menu_item->AddChild("OTHER"); parent_menu->AddChild("Quit", QUIT_CMD_EVENT); parent_menu->AddChild("Show Image", IMAGE_CMD_EVENT, FALSE); parent_menu->AddChild("ShowBlock Outlines", BLOCKS_CMD_EVENT, FALSE); parent_menu->AddChild("Show Baselines", BASELINES_CMD_EVENT, FALSE); parent_menu->AddChild("Uniform Display", UNIFORM_DISP_CMD_EVENT); parent_menu->AddChild("Refresh Display", REFRESH_CMD_EVENT); return root_menu_item; } /** * do_re_display() * * Redisplay page */ void Tesseract::do_re_display( BOOL8 (tesseract::Tesseract::*word_painter)(PAGE_RES_IT* pr_it)) { int block_count = 1; image_win->Clear(); if (display_image != 0) { image_win->Image(pix_binary_, 0, 0); } image_win->Brush(ScrollView::NONE); PAGE_RES_IT pr_it(current_page_res); for (WERD_RES* word = pr_it.word(); word != NULL; word = pr_it.forward()) { (this->*word_painter)(&pr_it); if (display_baselines && pr_it.row() != pr_it.prev_row()) pr_it.row()->row->plot_baseline(image_win, ScrollView::GREEN); if (display_blocks && pr_it.block() != pr_it.prev_block()) pr_it.block()->block->plot(image_win, block_count++, ScrollView::RED); } image_win->Update(); } /** * pgeditor_main() * * Top level editor operation: * Setup a new window and an according event handler * */ void Tesseract::pgeditor_main(int width, int height, PAGE_RES *page_res) { current_page_res = page_res; if (current_page_res->block_res_list.empty()) return; recog_done = false; stillRunning = true; build_image_window(width, height); word_display_mode.turn_on_bit(DF_EDGE_STEP); do_re_display(&tesseract::Tesseract::word_set_display); #ifndef GRAPHICS_DISABLED pe = new ParamsEditor(this, image_win); #endif PGEventHandler pgEventHandler(this); image_win->AddEventHandler(&pgEventHandler); image_win->AddMessageBox(); SVMenuNode* svMenuRoot = build_menu_new(); svMenuRoot->BuildMenu(image_win); image_win->SetVisible(true); image_win->AwaitEvent(SVET_DESTROY); image_win->AddEventHandler(NULL); } } // namespace tesseract /** * pgeditor_msg() * * Display a message - in the command window if there is one, or to stdout */ void pgeditor_msg( // message display const char *msg) { image_win->AddMessage(msg); } /** * pgeditor_show_point() * * Display the coordinates of a point in the command window */ void pgeditor_show_point( // display coords SVEvent *event) { image_win->AddMessage("Pointing at(%d, %d)", event->x, event->y); } /** * process_cmd_win_event() * * Process a command returned from the command window * (Just call the appropriate command handler) */ namespace tesseract { BOOL8 Tesseract::process_cmd_win_event( // UI command semantics inT32 cmd_event, // which menu item? char *new_value // any prompt data ) { char msg[160]; BOOL8 exit = FALSE; color_mode = CM_RAINBOW; // Run recognition on the full page if needed. switch (cmd_event) { case BLAMER_CMD_EVENT: case SHOW_SUBSCRIPT_CMD_EVENT: case SHOW_SUPERSCRIPT_CMD_EVENT: case SHOW_ITALIC_CMD_EVENT: case SHOW_BOLD_CMD_EVENT: case SHOW_UNDERLINE_CMD_EVENT: case SHOW_FIXEDPITCH_CMD_EVENT: case SHOW_SERIF_CMD_EVENT: case SHOW_SMALLCAPS_CMD_EVENT: case SHOW_DROPCAPS_CMD_EVENT: if (!recog_done) { recog_all_words(current_page_res, NULL, NULL, NULL, 0); recog_done = true; } break; default: break; } switch (cmd_event) { case NULL_CMD_EVENT: break; case CHANGE_DISP_CMD_EVENT: case DUMP_WERD_CMD_EVENT: case SHOW_POINT_CMD_EVENT: case SHOW_BLN_WERD_CMD_EVENT: case RECOG_WERDS: case RECOG_PSEUDO: case SHOW_BLOB_FEATURES: mode =(CMD_EVENTS) cmd_event; break; case DEBUG_WERD_CMD_EVENT: mode = DEBUG_WERD_CMD_EVENT; word_config_ = image_win->ShowInputDialog("Config File Name"); break; case BOUNDING_BOX_CMD_EVENT: if (new_value[0] == 'T') word_display_mode.turn_on_bit(DF_BOX); else word_display_mode.turn_off_bit(DF_BOX); mode = CHANGE_DISP_CMD_EVENT; break; case BLAMER_CMD_EVENT: if (new_value[0] == 'T') word_display_mode.turn_on_bit(DF_BLAMER); else word_display_mode.turn_off_bit(DF_BLAMER); do_re_display(&tesseract::Tesseract::word_display); mode = CHANGE_DISP_CMD_EVENT; break; case CORRECT_TEXT_CMD_EVENT: if (new_value[0] == 'T') word_display_mode.turn_on_bit(DF_TEXT); else word_display_mode.turn_off_bit(DF_TEXT); mode = CHANGE_DISP_CMD_EVENT; break; case POLYGONAL_CMD_EVENT: if (new_value[0] == 'T') word_display_mode.turn_on_bit(DF_POLYGONAL); else word_display_mode.turn_off_bit(DF_POLYGONAL); mode = CHANGE_DISP_CMD_EVENT; break; case BL_NORM_CMD_EVENT: if (new_value[0] == 'T') word_display_mode.turn_on_bit(DF_BN_POLYGONAL); else word_display_mode.turn_off_bit(DF_BN_POLYGONAL); mode = CHANGE_DISP_CMD_EVENT; break; case BITMAP_CMD_EVENT: if (new_value[0] == 'T') word_display_mode.turn_on_bit(DF_EDGE_STEP); else word_display_mode.turn_off_bit(DF_EDGE_STEP); mode = CHANGE_DISP_CMD_EVENT; break; case UNIFORM_DISP_CMD_EVENT: do_re_display(&tesseract::Tesseract::word_set_display); break; case IMAGE_CMD_EVENT: display_image =(new_value[0] == 'T'); do_re_display(&tesseract::Tesseract::word_display); break; case BLOCKS_CMD_EVENT: display_blocks =(new_value[0] == 'T'); do_re_display(&tesseract::Tesseract::word_display); break; case BASELINES_CMD_EVENT: display_baselines =(new_value[0] == 'T'); do_re_display(&tesseract::Tesseract::word_display); break; case SHOW_SUBSCRIPT_CMD_EVENT: color_mode = CM_SUBSCRIPT; do_re_display(&tesseract::Tesseract::word_display); break; case SHOW_SUPERSCRIPT_CMD_EVENT: color_mode = CM_SUPERSCRIPT; do_re_display(&tesseract::Tesseract::word_display); break; case SHOW_ITALIC_CMD_EVENT: color_mode = CM_ITALIC; do_re_display(&tesseract::Tesseract::word_display); break; case SHOW_BOLD_CMD_EVENT: color_mode = CM_BOLD; do_re_display(&tesseract::Tesseract::word_display); break; case SHOW_UNDERLINE_CMD_EVENT: color_mode = CM_UNDERLINE; do_re_display(&tesseract::Tesseract::word_display); break; case SHOW_FIXEDPITCH_CMD_EVENT: color_mode = CM_FIXEDPITCH; do_re_display(&tesseract::Tesseract::word_display); break; case SHOW_SERIF_CMD_EVENT: color_mode = CM_SERIF; do_re_display(&tesseract::Tesseract::word_display); break; case SHOW_SMALLCAPS_CMD_EVENT: color_mode = CM_SMALLCAPS; do_re_display(&tesseract::Tesseract::word_display); break; case SHOW_DROPCAPS_CMD_EVENT: color_mode = CM_DROPCAPS; do_re_display(&tesseract::Tesseract::word_display); break; case REFRESH_CMD_EVENT: do_re_display(&tesseract::Tesseract::word_display); break; case QUIT_CMD_EVENT: exit = TRUE; ScrollView::Exit(); break; default: sprintf(msg, "Unrecognised event " INT32FORMAT "(%s)", cmd_event, new_value); image_win->AddMessage(msg); break; } return exit; } /** * process_image_event() * * User has done something in the image window - mouse down or up. Work out * what it is and do something with it. * If DOWN - just remember where it was. * If UP - for each word in the selected area do the operation defined by * the current mode. */ void Tesseract::process_image_event( // action in image win const SVEvent &event) { // The following variable should remain static, since it is used by // debug editor, which uses a single Tesseract instance. static ICOORD down; ICOORD up; TBOX selection_box; char msg[80]; switch(event.type) { case SVET_SELECTION: if (event.type == SVET_SELECTION) { down.set_x(event.x + event.x_size); down.set_y(event.y + event.y_size); if (mode == SHOW_POINT_CMD_EVENT) show_point(current_page_res, event.x, event.y); } up.set_x(event.x); up.set_y(event.y); selection_box = TBOX(down, up); switch(mode) { case CHANGE_DISP_CMD_EVENT: process_selected_words( current_page_res, selection_box, &tesseract::Tesseract::word_blank_and_set_display); break; case DUMP_WERD_CMD_EVENT: process_selected_words(current_page_res, selection_box, &tesseract::Tesseract::word_dumper); break; case SHOW_BLN_WERD_CMD_EVENT: process_selected_words(current_page_res, selection_box, &tesseract::Tesseract::word_bln_display); break; case DEBUG_WERD_CMD_EVENT: debug_word(current_page_res, selection_box); break; case SHOW_POINT_CMD_EVENT: break; // ignore up event case RECOG_WERDS: image_win->AddMessage("Recogging selected words"); this->process_selected_words(current_page_res, selection_box, &Tesseract::recog_interactive); break; case RECOG_PSEUDO: image_win->AddMessage("Recogging selected blobs"); recog_pseudo_word(current_page_res, selection_box); break; case SHOW_BLOB_FEATURES: blob_feature_display(current_page_res, selection_box); break; default: sprintf(msg, "Mode %d not yet implemented", mode); image_win->AddMessage(msg); break; } default: break; } } /** * debug_word * * Process the whole image, but load word_config_ for the selected word(s). */ void Tesseract::debug_word(PAGE_RES* page_res, const TBOX &selection_box) { ResetAdaptiveClassifier(); recog_all_words(page_res, NULL, &selection_box, word_config_.string(), 0); } } // namespace tesseract /** * show_point() * * Show coords of point, blob bounding box, word bounding box and offset from * row baseline */ void show_point(PAGE_RES* page_res, float x, float y) { FCOORD pt(x, y); PAGE_RES_IT pr_it(page_res); const int kBufsize = 512; char msg[kBufsize]; char *msg_ptr = msg; msg_ptr += sprintf(msg_ptr, "Pt:(%0.3f, %0.3f) ", x, y); for (WERD_RES* word = pr_it.word(); word != NULL; word = pr_it.forward()) { if (pr_it.row() != pr_it.prev_row() && pr_it.row()->row->bounding_box().contains(pt)) { msg_ptr += sprintf(msg_ptr, "BL(x)=%0.3f ", pr_it.row()->row->base_line(x)); } if (word->word->bounding_box().contains(pt)) { TBOX box = word->word->bounding_box(); msg_ptr += sprintf(msg_ptr, "Wd(%d, %d)/(%d, %d) ", box.left(), box.bottom(), box.right(), box.top()); C_BLOB_IT cblob_it(word->word->cblob_list()); for (cblob_it.mark_cycle_pt(); !cblob_it.cycled_list(); cblob_it.forward()) { C_BLOB* cblob = cblob_it.data(); box = cblob->bounding_box(); if (box.contains(pt)) { msg_ptr += sprintf(msg_ptr, "CBlb(%d, %d)/(%d, %d) ", box.left(), box.bottom(), box.right(), box.top()); } } } } image_win->AddMessage(msg); } /********************************************************************** * WERD PROCESSOR FUNCTIONS * ======================== * * These routines are invoked by one or more of: * process_all_words() * process_selected_words() * or * process_all_words_it() * process_selected_words_it() * for each word to be processed **********************************************************************/ /** * word_blank_and_set_display() Word processor * * Blank display of word then redisplay word according to current display mode * settings */ #endif // GRAPHICS_DISABLED namespace tesseract { #ifndef GRAPHICS_DISABLED BOOL8 Tesseract:: word_blank_and_set_display(PAGE_RES_IT* pr_it) { pr_it->word()->word->bounding_box().plot(image_win, ScrollView::BLACK, ScrollView::BLACK); return word_set_display(pr_it); } /** * word_bln_display() * * Normalize word and display in word window */ BOOL8 Tesseract::word_bln_display(PAGE_RES_IT* pr_it) { WERD_RES* word_res = pr_it->word(); if (word_res->chopped_word == NULL) { // Setup word normalization parameters. word_res->SetupForRecognition(unicharset, this, BestPix(), tessedit_ocr_engine_mode, NULL, classify_bln_numeric_mode, textord_use_cjk_fp_model, poly_allow_detailed_fx, pr_it->row()->row, pr_it->block()->block); } bln_word_window_handle()->Clear(); display_bln_lines(bln_word_window_handle(), ScrollView::CYAN, 1.0, 0.0f, -1000.0f, 1000.0f); C_BLOB_IT it(word_res->word->cblob_list()); ScrollView::Color color = WERD::NextColor(ScrollView::BLACK); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { it.data()->plot_normed(word_res->denorm, color, ScrollView::BROWN, bln_word_window_handle()); color = WERD::NextColor(color); } bln_word_window_handle()->Update(); return TRUE; } /** * word_display() Word Processor * * Display a word according to its display modes */ BOOL8 Tesseract::word_display(PAGE_RES_IT* pr_it) { WERD_RES* word_res = pr_it->word(); WERD* word = word_res->word; TBOX word_bb; // word bounding box int word_height; // ht of word BB BOOL8 displayed_something = FALSE; float shift; // from bot left C_BLOB_IT c_it; // cblob iterator if (color_mode != CM_RAINBOW && word_res->box_word != NULL) { BoxWord* box_word = word_res->box_word; WERD_CHOICE* best_choice = word_res->best_choice; int length = box_word->length(); if (word_res->fontinfo == NULL) return false; const FontInfo& font_info = *word_res->fontinfo; for (int i = 0; i < length; ++i) { ScrollView::Color color = ScrollView::GREEN; switch (color_mode) { case CM_SUBSCRIPT: if (best_choice->BlobPosition(i) == SP_SUBSCRIPT) color = ScrollView::RED; break; case CM_SUPERSCRIPT: if (best_choice->BlobPosition(i) == SP_SUPERSCRIPT) color = ScrollView::RED; break; case CM_ITALIC: if (font_info.is_italic()) color = ScrollView::RED; break; case CM_BOLD: if (font_info.is_bold()) color = ScrollView::RED; break; case CM_FIXEDPITCH: if (font_info.is_fixed_pitch()) color = ScrollView::RED; break; case CM_SERIF: if (font_info.is_serif()) color = ScrollView::RED; break; case CM_SMALLCAPS: if (word_res->small_caps) color = ScrollView::RED; break; case CM_DROPCAPS: if (best_choice->BlobPosition(i) == SP_DROPCAP) color = ScrollView::RED; break; // TODO(rays) underline is currently completely unsupported. case CM_UNDERLINE: default: break; } image_win->Pen(color); TBOX box = box_word->BlobBox(i); image_win->Rectangle(box.left(), box.bottom(), box.right(), box.top()); } return true; } /* Note the double coercions of(COLOUR)((inT32)editor_image_word_bb_color) etc. are to keep the compiler happy. */ // display bounding box if (word->display_flag(DF_BOX)) { word->bounding_box().plot(image_win, (ScrollView::Color)((inT32) editor_image_word_bb_color), (ScrollView::Color)((inT32) editor_image_word_bb_color)); ScrollView::Color c = (ScrollView::Color) ((inT32) editor_image_blob_bb_color); image_win->Pen(c); c_it.set_to_list(word->cblob_list()); for (c_it.mark_cycle_pt(); !c_it.cycled_list(); c_it.forward()) c_it.data()->bounding_box().plot(image_win); displayed_something = TRUE; } // display edge steps if (word->display_flag(DF_EDGE_STEP)) { // edgesteps available word->plot(image_win); // rainbow colors displayed_something = TRUE; } // display poly approx if (word->display_flag(DF_POLYGONAL)) { // need to convert TWERD* tword = TWERD::PolygonalCopy(poly_allow_detailed_fx, word); tword->plot(image_win); delete tword; displayed_something = TRUE; } // Display correct text and blamer information. STRING text; STRING blame; if (word->display_flag(DF_TEXT) && word->text() != NULL) { text = word->text(); } if (word->display_flag(DF_BLAMER) && !(word_res->blamer_bundle != NULL && word_res->blamer_bundle->incorrect_result_reason() == IRR_CORRECT)) { text = ""; const BlamerBundle *blamer_bundle = word_res->blamer_bundle; if (blamer_bundle == NULL) { text += "NULL"; } else { text = blamer_bundle->TruthString(); } text += " -> "; STRING best_choice_str; if (word_res->best_choice == NULL) { best_choice_str = "NULL"; } else { word_res->best_choice->string_and_lengths(&best_choice_str, NULL); } text += best_choice_str; IncorrectResultReason reason = (blamer_bundle == NULL) ? IRR_PAGE_LAYOUT : blamer_bundle->incorrect_result_reason(); ASSERT_HOST(reason < IRR_NUM_REASONS) blame += " ["; blame += BlamerBundle::IncorrectReasonName(reason); blame += "]"; } if (text.length() > 0) { word_bb = word->bounding_box(); image_win->Pen(ScrollView::RED); word_height = word_bb.height(); int text_height = 0.50 * word_height; if (text_height > 20) text_height = 20; image_win->TextAttributes("Arial", text_height, false, false, false); shift = (word_height < word_bb.width()) ? 0.25 * word_height : 0.0f; image_win->Text(word_bb.left() + shift, word_bb.bottom() + 0.25 * word_height, text.string()); if (blame.length() > 0) { image_win->Text(word_bb.left() + shift, word_bb.bottom() + 0.25 * word_height - text_height, blame.string()); } displayed_something = TRUE; } if (!displayed_something) // display BBox anyway word->bounding_box().plot(image_win, (ScrollView::Color)((inT32) editor_image_word_bb_color), (ScrollView::Color)((inT32) editor_image_word_bb_color)); return TRUE; } #endif // GRAPHICS_DISABLED /** * word_dumper() * * Dump members to the debug window */ BOOL8 Tesseract::word_dumper(PAGE_RES_IT* pr_it) { if (pr_it->block()->block != NULL) { tprintf("\nBlock data...\n"); pr_it->block()->block->print(NULL, FALSE); } tprintf("\nRow data...\n"); pr_it->row()->row->print(NULL); tprintf("\nWord data...\n"); WERD_RES* word_res = pr_it->word(); word_res->word->print(); if (word_res->blamer_bundle != NULL && wordrec_debug_blamer && word_res->blamer_bundle->incorrect_result_reason() != IRR_CORRECT) { tprintf("Current blamer debug: %s\n", word_res->blamer_bundle->debug().string()); } return TRUE; } #ifndef GRAPHICS_DISABLED /** * word_set_display() Word processor * * Display word according to current display mode settings */ BOOL8 Tesseract::word_set_display(PAGE_RES_IT* pr_it) { WERD* word = pr_it->word()->word; word->set_display_flag(DF_BOX, word_display_mode.bit(DF_BOX)); word->set_display_flag(DF_TEXT, word_display_mode.bit(DF_TEXT)); word->set_display_flag(DF_POLYGONAL, word_display_mode.bit(DF_POLYGONAL)); word->set_display_flag(DF_EDGE_STEP, word_display_mode.bit(DF_EDGE_STEP)); word->set_display_flag(DF_BN_POLYGONAL, word_display_mode.bit(DF_BN_POLYGONAL)); word->set_display_flag(DF_BLAMER, word_display_mode.bit(DF_BLAMER)); return word_display(pr_it); } // page_res is non-const because the iterator doesn't know if you are going // to change the items it points to! Really a const here though. void Tesseract::blob_feature_display(PAGE_RES* page_res, const TBOX& selection_box) { PAGE_RES_IT* it = make_pseudo_word(page_res, selection_box); if (it != NULL) { WERD_RES* word_res = it->word(); word_res->x_height = it->row()->row->x_height(); word_res->SetupForRecognition(unicharset, this, BestPix(), tessedit_ocr_engine_mode, NULL, classify_bln_numeric_mode, textord_use_cjk_fp_model, poly_allow_detailed_fx, it->row()->row, it->block()->block); TWERD* bln_word = word_res->chopped_word; TBLOB* bln_blob = bln_word->blobs[0]; INT_FX_RESULT_STRUCT fx_info; GenericVector bl_features; GenericVector cn_features; Classify::ExtractFeatures(*bln_blob, classify_nonlinear_norm, &bl_features, &cn_features, &fx_info, NULL); // Display baseline features. ScrollView* bl_win = CreateFeatureSpaceWindow("BL Features", 512, 0); ClearFeatureSpaceWindow(baseline, bl_win); for (int f = 0; f < bl_features.size(); ++f) RenderIntFeature(bl_win, &bl_features[f], ScrollView::GREEN); bl_win->Update(); // Display cn features. ScrollView* cn_win = CreateFeatureSpaceWindow("CN Features", 512, 0); ClearFeatureSpaceWindow(character, cn_win); for (int f = 0; f < cn_features.size(); ++f) RenderIntFeature(cn_win, &cn_features[f], ScrollView::GREEN); cn_win->Update(); it->DeleteCurrentWord(); delete it; } } #endif // GRAPHICS_DISABLED } // namespace tesseract tesseract-3.04.01/ccmain/pgedit.h000066400000000000000000000067301266071204500165440ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: pgedit.h // Description: Page structure file editor // Author: Joern Wanke // Created: Wed Jul 18 10:05:01 PDT 2007 // // (C) Copyright 2007, 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 PGEDIT_H #define PGEDIT_H #include "ocrblock.h" #include "ocrrow.h" #include "werd.h" #include "rect.h" #include "params.h" #include "tesseractclass.h" class ScrollView; class SVMenuNode; struct SVEvent; // A small event handler class to process incoming events to // this window. class PGEventHandler : public SVEventHandler { public: PGEventHandler(tesseract::Tesseract* tess) : tess_(tess) { } void Notify(const SVEvent* sve); private: tesseract::Tesseract* tess_; }; extern BLOCK_LIST *current_block_list; extern STRING_VAR_H (editor_image_win_name, "EditorImage", "Editor image window name"); extern INT_VAR_H (editor_image_xpos, 590, "Editor image X Pos"); extern INT_VAR_H (editor_image_ypos, 10, "Editor image Y Pos"); extern INT_VAR_H (editor_image_height, 680, "Editor image height"); extern INT_VAR_H (editor_image_width, 655, "Editor image width"); extern INT_VAR_H (editor_image_word_bb_color, BLUE, "Word bounding box colour"); extern INT_VAR_H (editor_image_blob_bb_color, YELLOW, "Blob bounding box colour"); extern INT_VAR_H (editor_image_text_color, WHITE, "Correct text colour"); extern STRING_VAR_H (editor_dbwin_name, "EditorDBWin", "Editor debug window name"); extern INT_VAR_H (editor_dbwin_xpos, 50, "Editor debug window X Pos"); extern INT_VAR_H (editor_dbwin_ypos, 500, "Editor debug window Y Pos"); extern INT_VAR_H (editor_dbwin_height, 24, "Editor debug window height"); extern INT_VAR_H (editor_dbwin_width, 80, "Editor debug window width"); extern STRING_VAR_H (editor_word_name, "BlnWords", "BL normalised word window"); extern INT_VAR_H (editor_word_xpos, 60, "Word window X Pos"); extern INT_VAR_H (editor_word_ypos, 510, "Word window Y Pos"); extern INT_VAR_H (editor_word_height, 240, "Word window height"); extern INT_VAR_H (editor_word_width, 655, "Word window width"); extern double_VAR_H (editor_smd_scale_factor, 1.0, "Scaling for smd image"); ScrollView* bln_word_window_handle(); //return handle void build_image_window(int width, int height); void display_bln_lines(ScrollView window, ScrollView::Color colour, float scale_factor, float y_offset, float minx, float maxx); //function to call void pgeditor_msg( //message display const char *msg); void pgeditor_show_point( //display coords SVEvent *event); //put bln word in box void show_point(PAGE_RES* page_res, float x, float y); #endif tesseract-3.04.01/ccmain/recogtraining.cpp000066400000000000000000000213251266071204500204530ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: recogtraining.cpp // Description: Functions for ambiguity and parameter training. // Author: Daria Antonova // Created: Mon Aug 13 11:26:43 PDT 2009 // // (C) Copyright 2009, 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 "tesseractclass.h" #include "boxread.h" #include "control.h" #include "cutil.h" #include "host.h" #include "ratngs.h" #include "reject.h" #include "stopper.h" namespace tesseract { const inT16 kMaxBoxEdgeDiff = 2; // Sets flags necessary for recognition in the training mode. // Opens and returns the pointer to the output file. 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 // Explore all segmentations. getDict().stopper_no_acceptable_choices.set_value(1); } STRING output_fname = fname; const char *lastdot = strrchr(output_fname.string(), '.'); if (lastdot != NULL) output_fname[lastdot - output_fname.string()] = '\0'; output_fname += ".txt"; FILE *output_file = open_file(output_fname.string(), "a+"); return output_file; } // Copies the bounding box from page_res_it->word() to the given TBOX. bool read_t(PAGE_RES_IT *page_res_it, TBOX *tbox) { while (page_res_it->block() != NULL && page_res_it->word() == NULL) page_res_it->forward(); if (page_res_it->word() != NULL) { *tbox = page_res_it->word()->word->bounding_box(); // If tbox->left() is negative, the training image has vertical text and // all the coordinates of bounding boxes of page_res are rotated by 90 // degrees in a counterclockwise direction. We need to rotate the TBOX back // in order to compare with the TBOXes of box files. if (tbox->left() < 0) { tbox->rotate(FCOORD(0.0, -1.0)); } return true; } else { return false; } } // This function takes tif/box pair of files and runs recognition on the image, // while making sure that the word bounds that tesseract identified roughly // match to those specified by the input box file. For each word (ngram in a // single bounding box from the input box file) it outputs the ocred result, // the correct label, rating and certainty. void Tesseract::recog_training_segmented(const STRING &fname, PAGE_RES *page_res, volatile ETEXT_DESC *monitor, FILE *output_file) { STRING box_fname = fname; const char *lastdot = strrchr(box_fname.string(), '.'); if (lastdot != NULL) box_fname[lastdot - box_fname.string()] = '\0'; box_fname += ".box"; // ReadNextBox() will close box_file FILE *box_file = open_file(box_fname.string(), "r"); PAGE_RES_IT page_res_it; page_res_it.page_res = page_res; page_res_it.restart_page(); STRING label; // Process all the words on this page. TBOX tbox; // tesseract-identified box TBOX bbox; // box from the box file bool keep_going; int line_number = 0; int examined_words = 0; do { keep_going = read_t(&page_res_it, &tbox); keep_going &= ReadNextBox(applybox_page, &line_number, box_file, &label, &bbox); // Align bottom left points of the TBOXes. while (keep_going && !NearlyEqual(tbox.bottom(), bbox.bottom(), kMaxBoxEdgeDiff)) { if (bbox.bottom() < tbox.bottom()) { page_res_it.forward(); keep_going = read_t(&page_res_it, &tbox); } else { keep_going = ReadNextBox(applybox_page, &line_number, box_file, &label, &bbox); } } while (keep_going && !NearlyEqual(tbox.left(), bbox.left(), kMaxBoxEdgeDiff)) { if (bbox.left() > tbox.left()) { page_res_it.forward(); keep_going = read_t(&page_res_it, &tbox); } else { keep_going = ReadNextBox(applybox_page, &line_number, box_file, &label, &bbox); } } // OCR the word if top right points of the TBOXes are similar. if (keep_going && NearlyEqual(tbox.right(), bbox.right(), kMaxBoxEdgeDiff) && NearlyEqual(tbox.top(), bbox.top(), kMaxBoxEdgeDiff)) { ambigs_classify_and_output(label.string(), &page_res_it, output_file); examined_words++; } page_res_it.forward(); } while (keep_going); // Set up scripts on all of the words that did not get sent to // ambigs_classify_and_output. They all should have, but if all the // werd_res's don't get uch_sets, tesseract will crash when you try // to iterate over them. :-( int total_words = 0; for (page_res_it.restart_page(); page_res_it.block() != NULL; page_res_it.forward()) { if (page_res_it.word()) { if (page_res_it.word()->uch_set == NULL) page_res_it.word()->SetupFake(unicharset); total_words++; } } if (examined_words < 0.85 * total_words) { tprintf("TODO(antonova): clean up recog_training_segmented; " " It examined only a small fraction of the ambigs image.\n"); } tprintf("recog_training_segmented: examined %d / %d words.\n", 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 // best choice. void Tesseract::ambigs_classify_and_output(const char *label, PAGE_RES_IT* pr_it, FILE *output_file) { // Classify word. fflush(stdout); WordData word_data(*pr_it); SetupWordPassN(1, &word_data); classify_word_and_language(1, pr_it, &word_data); WERD_RES* werd_res = word_data.word; WERD_CHOICE *best_choice = werd_res->best_choice; ASSERT_HOST(best_choice != NULL); // Compute the number of unichars in the label. GenericVector encoding; if (!unicharset.encode_string(label, true, &encoding, NULL, NULL)) { tprintf("Not outputting illegal unichar %s\n", label); return; } // 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 tesseract-3.04.01/ccmain/reject.cpp000066400000000000000000000704521266071204500171010ustar00rootroot00000000000000/********************************************************************** * File: reject.cpp (Formerly reject.c) * Description: Rejection functions used in tessedit * Author: Phil Cheatle * Created: Wed Sep 23 16:50:21 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. * **********************************************************************/ #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #pragma warning(disable:4305) // int/float warnings #endif #include "tessvars.h" #ifdef __UNIX__ #include #include #endif #include "scanutils.h" #include #include #include "genericvector.h" #include "reject.h" #include "control.h" #include "docqual.h" #include "globaloc.h" // For err_exit. #include "globals.h" #include "helpers.h" #include "tesseractclass.h" // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif CLISTIZEH (STRING) CLISTIZE (STRING) /************************************************************************* * set_done() * * Set the done flag based on the word acceptability criteria *************************************************************************/ namespace tesseract { 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 (tessedit_rejection_debug) { tprintf("set_done(): done=%d\n", word->done); word->best_choice->print(""); } } /************************************************************************* * make_reject_map() * * Sets the done flag to indicate whether the resylt is acceptable. * * Sets a reject map for the word. *************************************************************************/ void Tesseract::make_reject_map(WERD_RES *word, ROW *row, inT16 pass) { int i; int offset; flip_0O(word); check_debug_pt(word, -1); // For trap only set_done(word, pass); // Set acceptance word->reject_map.initialise(word->best_choice->unichar_lengths().length()); reject_blanks(word); /* 0: Rays original heuristic - the baseline */ if (tessedit_reject_mode == 0) { if (!word->done) reject_poor_matches(word); } else if (tessedit_reject_mode == 5) { /* 5: Reject I/1/l from words where there is no strong contextual confirmation; the whole of any unacceptable words (incl PERM rej of dubious 1/I/ls); and the whole of any words which are very small */ if (kBlnXHeight / word->denorm.y_scale() <= min_sane_x_ht_pixels) { word->reject_map.rej_word_small_xht(); } else { one_ell_conflict(word, TRUE); /* Originally the code here just used the done flag. Now I have duplicated and unpacked the conditions for setting the done flag so that each mechanism can be turned on or off independently. This works WITHOUT affecting the done flag setting. */ if (rej_use_tess_accepted && !word->tess_accepted) word->reject_map.rej_word_not_tess_accepted (); if (rej_use_tess_blanks && (strchr (word->best_choice->unichar_string().string (), ' ') != NULL)) word->reject_map.rej_word_contains_blanks (); WERD_CHOICE* best_choice = word->best_choice; if (rej_use_good_perm) { if ((best_choice->permuter() == SYSTEM_DAWG_PERM || best_choice->permuter() == FREQ_DAWG_PERM || best_choice->permuter() == USER_DAWG_PERM) && (!rej_use_sensible_wd || acceptable_word_string(*word->uch_set, best_choice->unichar_string().string(), best_choice->unichar_lengths().string()) != AC_UNACCEPTABLE)) { // PASSED TEST } else if (best_choice->permuter() == NUMBER_PERM) { if (rej_alphas_in_number_perm) { for (i = 0, offset = 0; best_choice->unichar_string()[offset] != '\0'; offset += best_choice->unichar_lengths()[i++]) { if (word->reject_map[i].accepted() && word->uch_set->get_isalpha( best_choice->unichar_string().string() + offset, best_choice->unichar_lengths()[i])) word->reject_map[i].setrej_bad_permuter(); // rej alpha } } } else { word->reject_map.rej_word_bad_permuter(); } } /* Ambig word rejection was here once !!*/ } } else { tprintf("BAD tessedit_reject_mode\n"); err_exit(); } if (tessedit_image_border > -1) reject_edge_blobs(word); check_debug_pt (word, 10); if (tessedit_rejection_debug) { tprintf("Permuter Type = %d\n", word->best_choice->permuter ()); tprintf("Certainty: %f Rating: %f\n", word->best_choice->certainty (), word->best_choice->rating ()); tprintf("Dict word: %d\n", dict_word(*(word->best_choice))); } flip_hyphens(word); check_debug_pt(word, 20); } } // namespace tesseract void reject_blanks(WERD_RES *word) { inT16 i; inT16 offset; for (i = 0, offset = 0; word->best_choice->unichar_string()[offset] != '\0'; offset += word->best_choice->unichar_lengths()[i], i += 1) { if (word->best_choice->unichar_string()[offset] == ' ') //rej unrecognised blobs word->reject_map[i].setrej_tess_failure (); } } namespace tesseract { void Tesseract::reject_I_1_L(WERD_RES *word) { inT16 i; inT16 offset; for (i = 0, offset = 0; word->best_choice->unichar_string()[offset] != '\0'; offset += word->best_choice->unichar_lengths()[i], i += 1) { if (STRING (conflict_set_I_l_1). contains (word->best_choice->unichar_string()[offset])) { //rej 1Il conflict word->reject_map[i].setrej_1Il_conflict (); } } } } // namespace tesseract 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(); } } /********************************************************************** * compute_reject_threshold * * Set a rejection threshold for this word. * Initially this is a trivial function which looks for the largest * gap in the certainty value. **********************************************************************/ 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 gapstart = ratings[index]; } } } threshold = gapstart + bestgap / 2; return threshold; } /************************************************************************* * reject_edge_blobs() * * If the word is perilously close to the edge of the image, reject those blobs * in the word which are too close to the edge as they could be clipped. *************************************************************************/ namespace tesseract { void Tesseract::reject_edge_blobs(WERD_RES *word) { TBOX word_box = word->word->bounding_box(); // Use the box_word as it is already denormed back to image coordinates. int blobcount = word->box_word->length(); if (word_box.left() < tessedit_image_border || word_box.bottom() < tessedit_image_border || word_box.right() + tessedit_image_border > ImageWidth() - 1 || word_box.top() + tessedit_image_border > ImageHeight() - 1) { ASSERT_HOST(word->reject_map.length() == blobcount); for (int blobindex = 0; blobindex < blobcount; blobindex++) { TBOX blob_box = word->box_word->BlobBox(blobindex); if (blob_box.left() < tessedit_image_border || blob_box.bottom() < tessedit_image_border || blob_box.right() + tessedit_image_border > ImageWidth() - 1 || blob_box.top() + tessedit_image_border > ImageHeight() - 1) { word->reject_map[blobindex].setrej_edge_char(); // Close to edge } } } } /********************************************************************** * one_ell_conflict() * * Identify words where there is a potential I/l/1 error. * - A bundle of contextual heuristics! **********************************************************************/ BOOL8 Tesseract::one_ell_conflict(WERD_RES *word_res, BOOL8 update_map) { const char *word; const char *lengths; inT16 word_len; //its length inT16 first_alphanum_index_; inT16 first_alphanum_offset_; inT16 i; inT16 offset; BOOL8 non_conflict_set_char; //non conf set a/n? BOOL8 conflict = FALSE; BOOL8 allow_1s; ACCEPTABLE_WERD_TYPE word_type; BOOL8 dict_perm_type; BOOL8 dict_word_ok; int dict_word_type; word = word_res->best_choice->unichar_string().string (); lengths = word_res->best_choice->unichar_lengths().string(); word_len = strlen (lengths); /* If there are no occurrences of the conflict set characters then the word is OK. */ if (strpbrk (word, conflict_set_I_l_1.string ()) == NULL) return FALSE; /* There is a conflict if there are NO other (confirmed) alphanumerics apart from those in the conflict set. */ for (i = 0, offset = 0, non_conflict_set_char = FALSE; (i < word_len) && !non_conflict_set_char; offset += lengths[i++]) non_conflict_set_char = (word_res->uch_set->get_isalpha(word + offset, lengths[i]) || word_res->uch_set->get_isdigit(word + offset, lengths[i])) && !STRING (conflict_set_I_l_1).contains (word[offset]); if (!non_conflict_set_char) { if (update_map) reject_I_1_L(word_res); return TRUE; } /* If the word is accepted by a dawg permuter, and the first alpha character is "I" or "l", check to see if the alternative is also a dawg word. If it is, then there is a potential error otherwise the word is ok. */ dict_perm_type = (word_res->best_choice->permuter () == SYSTEM_DAWG_PERM) || (word_res->best_choice->permuter () == USER_DAWG_PERM) || (rej_trust_doc_dawg && (word_res->best_choice->permuter () == DOC_DAWG_PERM)) || (word_res->best_choice->permuter () == FREQ_DAWG_PERM); dict_word_type = dict_word(*(word_res->best_choice)); dict_word_ok = (dict_word_type > 0) && (rej_trust_doc_dawg || (dict_word_type != DOC_DAWG_PERM)); if ((rej_1Il_use_dict_word && dict_word_ok) || (rej_1Il_trust_permuter_type && dict_perm_type) || (dict_perm_type && dict_word_ok)) { first_alphanum_index_ = first_alphanum_index (word, lengths); first_alphanum_offset_ = first_alphanum_offset (word, lengths); if (lengths[first_alphanum_index_] == 1 && word[first_alphanum_offset_] == 'I') { word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'l'; if (safe_dict_word(word_res) > 0) { word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'I'; if (update_map) word_res->reject_map[first_alphanum_index_]. setrej_1Il_conflict(); return TRUE; } else { word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'I'; return FALSE; } } if (lengths[first_alphanum_index_] == 1 && word[first_alphanum_offset_] == 'l') { word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'I'; if (safe_dict_word(word_res) > 0) { word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'l'; if (update_map) word_res->reject_map[first_alphanum_index_]. setrej_1Il_conflict(); return TRUE; } else { word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'l'; return FALSE; } } return FALSE; } /* NEW 1Il code. The old code relied on permuter types too much. In fact, tess will use TOP_CHOICE permute for good things like "palette". In this code the string is examined independently to see if it looks like a well formed word. */ /* REGARDLESS OF PERMUTER, see if flipping a leading I/l generates a dictionary word. */ first_alphanum_index_ = first_alphanum_index (word, lengths); first_alphanum_offset_ = first_alphanum_offset (word, lengths); if (lengths[first_alphanum_index_] == 1 && word[first_alphanum_offset_] == 'l') { word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'I'; if (safe_dict_word(word_res) > 0) return FALSE; else word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'l'; } else if (lengths[first_alphanum_index_] == 1 && word[first_alphanum_offset_] == 'I') { word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'l'; if (safe_dict_word(word_res) > 0) return FALSE; else word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'I'; } /* For strings containing digits: If there are no alphas OR the numeric permuter liked the word, reject any non 1 conflict chs Else reject all conflict chs */ if (word_contains_non_1_digit (word, lengths)) { allow_1s = (alpha_count (word, lengths) == 0) || (word_res->best_choice->permuter () == NUMBER_PERM); inT16 offset; conflict = FALSE; for (i = 0, offset = 0; word[offset] != '\0'; offset += word_res->best_choice->unichar_lengths()[i++]) { if ((!allow_1s || (word[offset] != '1')) && STRING (conflict_set_I_l_1).contains (word[offset])) { if (update_map) word_res->reject_map[i].setrej_1Il_conflict (); conflict = TRUE; } } return conflict; } /* For anything else. See if it conforms to an acceptable word type. If so, treat accordingly. */ word_type = acceptable_word_string(*word_res->uch_set, word, lengths); if ((word_type == AC_LOWER_CASE) || (word_type == AC_INITIAL_CAP)) { first_alphanum_index_ = first_alphanum_index (word, lengths); first_alphanum_offset_ = first_alphanum_offset (word, lengths); if (STRING (conflict_set_I_l_1).contains (word[first_alphanum_offset_])) { if (update_map) word_res->reject_map[first_alphanum_index_]. setrej_1Il_conflict (); return TRUE; } else return FALSE; } else if (word_type == AC_UPPER_CASE) { return FALSE; } else { if (update_map) reject_I_1_L(word_res); return TRUE; } } inT16 Tesseract::first_alphanum_index(const char *word, const char *word_lengths) { inT16 i; inT16 offset; for (i = 0, offset = 0; word[offset] != '\0'; offset += word_lengths[i++]) { if (unicharset.get_isalpha(word + offset, word_lengths[i]) || unicharset.get_isdigit(word + offset, word_lengths[i])) return i; } return -1; } inT16 Tesseract::first_alphanum_offset(const char *word, const char *word_lengths) { inT16 i; inT16 offset; for (i = 0, offset = 0; word[offset] != '\0'; offset += word_lengths[i++]) { if (unicharset.get_isalpha(word + offset, word_lengths[i]) || unicharset.get_isdigit(word + offset, word_lengths[i])) return offset; } return -1; } inT16 Tesseract::alpha_count(const char *word, const char *word_lengths) { inT16 i; inT16 offset; inT16 count = 0; for (i = 0, offset = 0; word[offset] != '\0'; offset += word_lengths[i++]) { if (unicharset.get_isalpha (word + offset, word_lengths[i])) count++; } return count; } BOOL8 Tesseract::word_contains_non_1_digit(const char *word, const char *word_lengths) { inT16 i; inT16 offset; for (i = 0, offset = 0; word[offset] != '\0'; offset += word_lengths[i++]) { if (unicharset.get_isdigit (word + offset, word_lengths[i]) && (word_lengths[i] != 1 || word[offset] != '1')) return TRUE; } return FALSE; } /************************************************************************* * dont_allow_1Il() * Don't unreject LONE accepted 1Il conflict set chars *************************************************************************/ void Tesseract::dont_allow_1Il(WERD_RES *word) { int i = 0; int offset; int word_len = word->reject_map.length(); const char *s = word->best_choice->unichar_string().string(); const char *lengths = word->best_choice->unichar_lengths().string(); BOOL8 accepted_1Il = FALSE; for (i = 0, offset = 0; i < word_len; offset += word->best_choice->unichar_lengths()[i++]) { if (word->reject_map[i].accepted()) { if (STRING(conflict_set_I_l_1).contains(s[offset])) { accepted_1Il = TRUE; } else { if (word->uch_set->get_isalpha(s + offset, lengths[i]) || word->uch_set->get_isdigit(s + offset, lengths[i])) return; // >=1 non 1Il ch accepted } } } if (!accepted_1Il) return; //Nothing to worry about for (i = 0, offset = 0; i < word_len; offset += word->best_choice->unichar_lengths()[i++]) { if (STRING(conflict_set_I_l_1).contains(s[offset]) && word->reject_map[i].accepted()) word->reject_map[i].setrej_postNN_1Il(); } } inT16 Tesseract::count_alphanums(WERD_RES *word_res) { int count = 0; const WERD_CHOICE *best_choice = word_res->best_choice; for (int i = 0; i < word_res->reject_map.length(); ++i) { if ((word_res->reject_map[i].accepted()) && (word_res->uch_set->get_isalpha(best_choice->unichar_id(i)) || word_res->uch_set->get_isdigit(best_choice->unichar_id(i)))) { count++; } } return count; } // reject all if most rejected. void Tesseract::reject_mostly_rejects(WERD_RES *word) { /* Reject the whole of the word if the fraction of rejects exceeds a limit */ if ((float) word->reject_map.reject_count() / word->reject_map.length() >= rej_whole_of_mostly_reject_word_fract) word->reject_map.rej_word_mostly_rej(); } BOOL8 Tesseract::repeated_nonalphanum_wd(WERD_RES *word, ROW *row) { inT16 char_quality; inT16 accepted_char_quality; if (word->best_choice->unichar_lengths().length() <= 1) return FALSE; if (!STRING(ok_repeated_ch_non_alphanum_wds). contains(word->best_choice->unichar_string()[0])) return FALSE; UNICHAR_ID uch_id = word->best_choice->unichar_id(0); for (int i = 1; i < word->best_choice->length(); ++i) { if (word->best_choice->unichar_id(i) != uch_id) return FALSE; } word_char_quality(word, row, &char_quality, &accepted_char_quality); if ((word->best_choice->unichar_lengths().length () == char_quality) && (char_quality == accepted_char_quality)) return TRUE; else return FALSE; } inT16 Tesseract::safe_dict_word(const WERD_RES *werd_res) { const WERD_CHOICE &word = *werd_res->best_choice; int dict_word_type = werd_res->tesseract->dict_word(word); return dict_word_type == DOC_DAWG_PERM ? 0 : dict_word_type; } // 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; int prev_right = -9999; int next_left; TBOX out_box; float aspect_ratio; if (tessedit_lower_flip_hyphen <= 1) return; int num_blobs = word_res->rebuild_word->NumBlobs(); UNICHAR_ID unichar_dash = word_res->uch_set->unichar_to_id("-"); 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 (i + 1 == num_blobs) next_left = 9999; else next_left = word_res->rebuild_word->blobs[i + 1]->bounding_box().left(); // Don't 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)) { aspect_ratio = out_box.width() / (float) out_box.height(); if (word_res->uch_set->eq(best_choice->unichar_id(i), ".")) { if (aspect_ratio >= tessedit_upper_flip_hyphen && word_res->uch_set->contains_unichar_id(unichar_dash) && word_res->uch_set->get_enabled(unichar_dash)) { /* Certain HYPHEN */ best_choice->set_unichar_id(unichar_dash, i); if (word_res->reject_map[i].rejected()) word_res->reject_map[i].setrej_hyphen_accept(); } if ((aspect_ratio > tessedit_lower_flip_hyphen) && word_res->reject_map[i].accepted()) //Suspected HYPHEN word_res->reject_map[i].setrej_hyphen (); } else if (best_choice->unichar_id(i) == unichar_dash) { if ((aspect_ratio >= tessedit_upper_flip_hyphen) && (word_res->reject_map[i].rejected())) word_res->reject_map[i].setrej_hyphen_accept(); //Certain HYPHEN if ((aspect_ratio <= tessedit_lower_flip_hyphen) && (word_res->reject_map[i].accepted())) //Suspected HYPHEN word_res->reject_map[i].setrej_hyphen(); } } prev_right = out_box.right(); } } // 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; TBOX out_box; if (!tessedit_flip_0O) return; 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(); if ((out_box.top() < kBlnBaselineOffset + kBlnXHeight) || (out_box.bottom() > kBlnBaselineOffset + kBlnXHeight / 4)) return; //Beware words with sub/superscripts } } UNICHAR_ID unichar_0 = word_res->uch_set->unichar_to_id("0"); UNICHAR_ID unichar_O = word_res->uch_set->unichar_to_id("O"); if (unichar_0 == INVALID_UNICHAR_ID || !word_res->uch_set->get_enabled(unichar_0) || unichar_O == INVALID_UNICHAR_ID || !word_res->uch_set->get_enabled(unichar_O)) { return; // 0 or O are not present/enabled in unicharset } for (i = 1; i < best_choice->length(); ++i) { if (best_choice->unichar_id(i) == unichar_0 || best_choice->unichar_id(i) == unichar_O) { /* A0A */ if ((i+1) < best_choice->length() && non_O_upper(*word_res->uch_set, best_choice->unichar_id(i-1)) && non_O_upper(*word_res->uch_set, best_choice->unichar_id(i+1))) { best_choice->set_unichar_id(unichar_O, i); } /* A00A */ if (non_O_upper(*word_res->uch_set, best_choice->unichar_id(i-1)) && (i+1) < best_choice->length() && (best_choice->unichar_id(i+1) == unichar_0 || best_choice->unichar_id(i+1) == unichar_O) && (i+2) < best_choice->length() && non_O_upper(*word_res->uch_set, best_choice->unichar_id(i+2))) { best_choice->set_unichar_id(unichar_O, i); i++; } /* AA0 */ if ((i > 1) && non_O_upper(*word_res->uch_set, best_choice->unichar_id(i-2)) && non_O_upper(*word_res->uch_set, best_choice->unichar_id(i-1)) && (((i+1) < best_choice->length() && !word_res->uch_set->get_isdigit(best_choice->unichar_id(i+1)) && !word_res->uch_set->eq(best_choice->unichar_id(i+1), "l") && !word_res->uch_set->eq(best_choice->unichar_id(i+1), "I")) || (i == best_choice->length() - 1))) { best_choice->set_unichar_id(unichar_O, i); } /* 9O9 */ if (non_0_digit(*word_res->uch_set, best_choice->unichar_id(i-1)) && (i+1) < best_choice->length() && non_0_digit(*word_res->uch_set, best_choice->unichar_id(i+1))) { best_choice->set_unichar_id(unichar_0, i); } /* 9OOO */ if (non_0_digit(*word_res->uch_set, best_choice->unichar_id(i-1)) && (i+2) < best_choice->length() && (best_choice->unichar_id(i+1) == unichar_0 || best_choice->unichar_id(i+1) == unichar_O) && (best_choice->unichar_id(i+2) == unichar_0 || best_choice->unichar_id(i+2) == unichar_O)) { best_choice->set_unichar_id(unichar_0, i); best_choice->set_unichar_id(unichar_0, i+1); best_choice->set_unichar_id(unichar_0, i+2); i += 2; } /* 9OO */ if (non_0_digit(*word_res->uch_set, best_choice->unichar_id(i-1)) && (i+2) < best_choice->length() && (best_choice->unichar_id(i+1) == unichar_0 || best_choice->unichar_id(i+1) == unichar_O) && !word_res->uch_set->get_isupper(best_choice->unichar_id(i+2))) { best_choice->set_unichar_id(unichar_0, i); best_choice->set_unichar_id(unichar_0, i+1); i++; } /* 9O */ if (non_0_digit(*word_res->uch_set, best_choice->unichar_id(i-1)) && (i+1) < best_choice->length() && !word_res->uch_set->get_isupper(best_choice->unichar_id(i+1))) { best_choice->set_unichar_id(unichar_0, i); } /* 9[.,]OOO.. */ if ((i > 1) && (word_res->uch_set->eq(best_choice->unichar_id(i-1), ".") || word_res->uch_set->eq(best_choice->unichar_id(i-1), ",")) && (word_res->uch_set->get_isdigit(best_choice->unichar_id(i-2)) || best_choice->unichar_id(i-2) == unichar_O)) { if (best_choice->unichar_id(i-2) == unichar_O) { best_choice->set_unichar_id(unichar_0, i-2); } while (i < best_choice->length() && (best_choice->unichar_id(i) == unichar_O || best_choice->unichar_id(i) == unichar_0)) { best_choice->set_unichar_id(unichar_0, i); i++; } i--; } } } } BOOL8 Tesseract::non_O_upper(const UNICHARSET& ch_set, UNICHAR_ID unichar_id) { return ch_set.get_isupper(unichar_id) && !ch_set.eq(unichar_id, "O"); } BOOL8 Tesseract::non_0_digit(const UNICHARSET& ch_set, UNICHAR_ID unichar_id) { return ch_set.get_isdigit(unichar_id) && !ch_set.eq(unichar_id, "0"); } } // namespace tesseract tesseract-3.04.01/ccmain/reject.h000066400000000000000000000025621266071204500165430ustar00rootroot00000000000000/********************************************************************** * File: reject.h (Formerly reject.h) * Description: Rejection functions used in tessedit * Author: Phil Cheatle * Created: Wed Sep 23 16:50:21 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 REJECT_H #define REJECT_H #include "params.h" #include "pageres.h" void reject_blanks(WERD_RES *word); 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); void flip_0O(WERD_RES *word); BOOL8 non_0_digit(const char* str, int length); #endif tesseract-3.04.01/ccmain/resultiterator.cpp000066400000000000000000000570601266071204500207150ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: resultiterator.cpp // Description: Iterator for tesseract results that is capable of // iterating in proper reading order over Bi Directional // (e.g. mixed Hebrew and English) text. // Author: David Eger // Created: Fri May 27 13:58:06 PST 2011 // // (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. // /////////////////////////////////////////////////////////////////////// #include "resultiterator.h" #include "allheaders.h" #include "pageres.h" #include "strngs.h" #include "tesseractclass.h" #include "unicharset.h" #include "unicodes.h" namespace tesseract { ResultIterator::ResultIterator(const LTRResultIterator &resit) : LTRResultIterator(resit) { in_minor_direction_ = false; at_beginning_of_minor_run_ = false; preserve_interword_spaces_ = false; BoolParam *p = ParamUtils::FindParam( "preserve_interword_spaces", GlobalParams()->bool_params, tesseract_->params()->bool_params); if (p != NULL) preserve_interword_spaces_ = (bool)(*p); current_paragraph_is_ltr_ = CurrentParagraphIsLtr(); MoveToLogicalStartOfTextline(); } ResultIterator *ResultIterator::StartOfParagraph( const LTRResultIterator &resit) { return new ResultIterator(resit); } bool ResultIterator::ParagraphIsLtr() const { return current_paragraph_is_ltr_; } bool ResultIterator::CurrentParagraphIsLtr() const { if (!it_->word()) return true; // doesn't matter. LTRResultIterator it(*this); it.RestartParagraph(); // Try to figure out the ltr-ness of the paragraph. The rules below // make more sense in the context of a difficult paragraph example. // Here we denote {ltr characters, RTL CHARACTERS}: // // "don't go in there!" DAIS EH // EHT OTNI DEPMUJ FELSMIH NEHT DNA // .GNIDLIUB GNINRUB // // On the first line, the left-most word is LTR and the rightmost word // is RTL. Thus, we are better off taking the majority direction for // the whole paragraph contents. So instead of "the leftmost word is LTR" // indicating an LTR paragraph, we use a heuristic about what RTL paragraphs // would not do: Typically an RTL paragraph would *not* start with an LTR // word. So our heuristics are as follows: // // (1) If the first text line has an RTL word in the left-most position // it is RTL. // (2) If the first text line has an LTR word in the right-most position // it is LTR. // (3) If neither of the above is true, take the majority count for the // paragraph -- if there are more rtl words, it is RTL. If there // are more LTR words, it's LTR. bool leftmost_rtl = it.WordDirection() == DIR_RIGHT_TO_LEFT; bool rightmost_ltr = it.WordDirection() == DIR_LEFT_TO_RIGHT; int num_ltr, num_rtl; num_rtl = leftmost_rtl ? 1 : 0; num_ltr = (it.WordDirection() == DIR_LEFT_TO_RIGHT) ? 1 : 0; for (it.Next(RIL_WORD); !it.Empty(RIL_WORD) && !it.IsAtBeginningOf(RIL_TEXTLINE); it.Next(RIL_WORD)) { StrongScriptDirection dir = it.WordDirection(); rightmost_ltr = (dir == DIR_LEFT_TO_RIGHT); num_rtl += (dir == DIR_RIGHT_TO_LEFT) ? 1 : 0; num_ltr += rightmost_ltr ? 1 : 0; } if (leftmost_rtl) return false; if (rightmost_ltr) return true; // First line is ambiguous. Take statistics on the whole paragraph. if (!it.Empty(RIL_WORD) && !it.IsAtBeginningOf(RIL_PARA)) do { StrongScriptDirection dir = it.WordDirection(); num_rtl += (dir == DIR_RIGHT_TO_LEFT) ? 1 : 0; num_ltr += (dir == DIR_LEFT_TO_RIGHT) ? 1 : 0; } while (it.Next(RIL_WORD) && !it.IsAtBeginningOf(RIL_PARA)); return num_ltr >= num_rtl; } const int ResultIterator::kMinorRunStart = -1; const int ResultIterator::kMinorRunEnd = -2; const int ResultIterator::kComplexWord = -3; void ResultIterator::CalculateBlobOrder( GenericVector *blob_indices) const { bool context_is_ltr = current_paragraph_is_ltr_ ^ in_minor_direction_; blob_indices->clear(); if (Empty(RIL_WORD)) return; if (context_is_ltr || it_->word()->UnicharsInReadingOrder()) { // Easy! just return the blobs in order; for (int i = 0; i < word_length_; i++) blob_indices->push_back(i); return; } // The blobs are in left-to-right order, but the current reading context // is right-to-left. const int U_LTR = UNICHARSET::U_LEFT_TO_RIGHT; const int U_RTL = UNICHARSET::U_RIGHT_TO_LEFT; const int U_EURO_NUM = UNICHARSET::U_EUROPEAN_NUMBER; const int U_EURO_NUM_SEP = UNICHARSET::U_EUROPEAN_NUMBER_SEPARATOR; const int U_EURO_NUM_TERM = UNICHARSET::U_EUROPEAN_NUMBER_TERMINATOR; const int U_COMMON_NUM_SEP = UNICHARSET::U_COMMON_NUMBER_SEPARATOR; const int U_OTHER_NEUTRAL = UNICHARSET::U_OTHER_NEUTRAL; // Step 1: Scan for and mark European Number sequences // [:ET:]*[:EN:]+(([:ES:]|[:CS:])?[:EN:]+)*[:ET:]* GenericVector letter_types; for (int i = 0; i < word_length_; i++) { letter_types.push_back(it_->word()->SymbolDirection(i)); } // Convert a single separtor sandwiched between two EN's into an EN. for (int i = 0; i + 2 < word_length_; i++) { if (letter_types[i] == U_EURO_NUM && letter_types[i + 2] == U_EURO_NUM && (letter_types[i + 1] == U_EURO_NUM_SEP || letter_types[i + 1] == U_COMMON_NUM_SEP)) { letter_types[i + 1] = U_EURO_NUM; } } // Scan for sequences of European Number Terminators around ENs and convert // them to ENs. for (int i = 0; i < word_length_; i++) { if (letter_types[i] == U_EURO_NUM_TERM) { int j = i + 1; while (j < word_length_ && letter_types[j] == U_EURO_NUM_TERM) { j++; } if (j < word_length_ && letter_types[j] == U_EURO_NUM) { // The sequence [i..j] should be converted to all European Numbers. for (int k = i; k < j; k++) letter_types[k] = U_EURO_NUM; } j = i - 1; while (j > -1 && letter_types[j] == U_EURO_NUM_TERM) { j--; } if (j > -1 && letter_types[j] == U_EURO_NUM) { // The sequence [j..i] should be converted to all European Numbers. for (int k = j; k <= i; k++) letter_types[k] = U_EURO_NUM; } } } // Step 2: Convert all remaining types to either L or R. // Sequences ([:L:]|[:EN:])+ (([:CS:]|[:ON:])+ ([:L:]|[:EN:])+)* -> L. // All other are R. for (int i = 0; i < word_length_;) { int ti = letter_types[i]; if (ti == U_LTR || ti == U_EURO_NUM) { // Left to right sequence; scan to the end of it. int last_good = i; for (int j = i + 1; j < word_length_; j++) { int tj = letter_types[j]; if (tj == U_LTR || tj == U_EURO_NUM) { last_good = j; } else if (tj == U_COMMON_NUM_SEP || tj == U_OTHER_NEUTRAL) { // do nothing. } else { break; } } // [i..last_good] is the L sequence for (int k = i; k <= last_good; k++) letter_types[k] = U_LTR; i = last_good + 1; } else { letter_types[i] = U_RTL; i++; } } // At this point, letter_types is entirely U_LTR or U_RTL. for (int i = word_length_ - 1; i >= 0;) { if (letter_types[i] == U_RTL) { blob_indices->push_back(i); i--; } else { // left to right sequence. scan to the beginning. int j = i - 1; for (; j >= 0 && letter_types[j] != U_RTL; j--) { } // pass // Now (j, i] is LTR for (int k = j + 1; k <= i; k++) blob_indices->push_back(k); i = j; } } ASSERT_HOST(blob_indices->size() == word_length_); } static void PrintScriptDirs(const GenericVector &dirs) { for (int i = 0; i < dirs.size(); i++) { switch (dirs[i]) { case DIR_NEUTRAL: tprintf ("N "); break; case DIR_LEFT_TO_RIGHT: tprintf("L "); break; case DIR_RIGHT_TO_LEFT: tprintf("R "); break; case DIR_MIX: tprintf("Z "); break; default: tprintf("? "); break; } } tprintf("\n"); } void ResultIterator::CalculateTextlineOrder( bool paragraph_is_ltr, const LTRResultIterator &resit, GenericVectorEqEq *word_indices) const { GenericVector directions; CalculateTextlineOrder(paragraph_is_ltr, resit, &directions, word_indices); } void ResultIterator::CalculateTextlineOrder( bool paragraph_is_ltr, const LTRResultIterator &resit, GenericVector *dirs_arg, GenericVectorEqEq *word_indices) const { GenericVector dirs; GenericVector *directions; directions = (dirs_arg != NULL) ? dirs_arg : &dirs; directions->truncate(0); // A LTRResultIterator goes strictly left-to-right word order. LTRResultIterator ltr_it(resit); ltr_it.RestartRow(); if (ltr_it.Empty(RIL_WORD)) return; do { directions->push_back(ltr_it.WordDirection()); } while (ltr_it.Next(RIL_WORD) && !ltr_it.IsAtBeginningOf(RIL_TEXTLINE)); word_indices->truncate(0); CalculateTextlineOrder(paragraph_is_ltr, *directions, word_indices); } void ResultIterator::CalculateTextlineOrder( bool paragraph_is_ltr, const GenericVector &word_dirs, GenericVectorEqEq *reading_order) { reading_order->truncate(0); if (word_dirs.size() == 0) return; // Take all of the runs of minor direction words and insert them // in reverse order. int minor_direction, major_direction, major_step, start, end; if (paragraph_is_ltr) { start = 0; end = word_dirs.size(); major_step = 1; major_direction = DIR_LEFT_TO_RIGHT; minor_direction = DIR_RIGHT_TO_LEFT; } else { start = word_dirs.size() - 1; end = -1; major_step = -1; major_direction = DIR_RIGHT_TO_LEFT; minor_direction = DIR_LEFT_TO_RIGHT; // Special rule: if there are neutral words at the right most side // of a line adjacent to a left-to-right word in the middle of the // line, we interpret the end of the line as a single LTR sequence. if (word_dirs[start] == DIR_NEUTRAL) { int neutral_end = start; while (neutral_end > 0 && word_dirs[neutral_end] == DIR_NEUTRAL) { neutral_end--; } if (neutral_end >= 0 && word_dirs[neutral_end] == DIR_LEFT_TO_RIGHT) { // LTR followed by neutrals. // Scan for the beginning of the minor left-to-right run. int left = neutral_end; for (int i = left; i >= 0 && word_dirs[i] != DIR_RIGHT_TO_LEFT; i--) { if (word_dirs[i] == DIR_LEFT_TO_RIGHT) left = i; } reading_order->push_back(kMinorRunStart); for (int i = left; i < word_dirs.size(); i++) { reading_order->push_back(i); if (word_dirs[i] == DIR_MIX) reading_order->push_back(kComplexWord); } reading_order->push_back(kMinorRunEnd); start = left - 1; } } } for (int i = start; i != end;) { if (word_dirs[i] == minor_direction) { int j = i; while (j != end && word_dirs[j] != major_direction) j += major_step; if (j == end) j -= major_step; while (j != i && word_dirs[j] != minor_direction) j -= major_step; // [j..i] is a minor direction run. reading_order->push_back(kMinorRunStart); for (int k = j; k != i; k -= major_step) { reading_order->push_back(k); } reading_order->push_back(i); reading_order->push_back(kMinorRunEnd); i = j + major_step; } else { reading_order->push_back(i); if (word_dirs[i] == DIR_MIX) reading_order->push_back(kComplexWord); i += major_step; } } } int ResultIterator::LTRWordIndex() const { int this_word_index = 0; LTRResultIterator textline(*this); textline.RestartRow(); while (!textline.PositionedAtSameWord(it_)) { this_word_index++; textline.Next(RIL_WORD); } return this_word_index; } void ResultIterator::MoveToLogicalStartOfWord() { if (word_length_ == 0) { BeginWord(0); return; } GenericVector blob_order; CalculateBlobOrder(&blob_order); if (blob_order.size() == 0 || blob_order[0] == 0) return; BeginWord(blob_order[0]); } bool ResultIterator::IsAtFinalSymbolOfWord() const { if (!it_->word()) return true; GenericVector blob_order; CalculateBlobOrder(&blob_order); return blob_order.size() == 0 || blob_order.back() == blob_index_; } bool ResultIterator::IsAtFirstSymbolOfWord() const { if (!it_->word()) return true; GenericVector blob_order; CalculateBlobOrder(&blob_order); return blob_order.size() == 0 || blob_order[0] == blob_index_; } void ResultIterator::AppendSuffixMarks(STRING *text) const { if (!it_->word()) return; bool reading_direction_is_ltr = current_paragraph_is_ltr_ ^ in_minor_direction_; // scan forward to see what meta-information the word ordering algorithm // left us. // If this word is at the *end* of a minor run, insert the other // direction's mark; else if this was a complex word, insert the // current reading order's mark. GenericVectorEqEq textline_order; CalculateTextlineOrder(current_paragraph_is_ltr_, *this, &textline_order); int this_word_index = LTRWordIndex(); int i = textline_order.get_index(this_word_index); if (i < 0) return; int last_non_word_mark = 0; for (i++; i < textline_order.size() && textline_order[i] < 0; i++) { last_non_word_mark = textline_order[i]; } if (last_non_word_mark == kComplexWord) { *text += reading_direction_is_ltr ? kLRM : kRLM; } else if (last_non_word_mark == kMinorRunEnd) { if (current_paragraph_is_ltr_) { *text += kLRM; } else { *text += kRLM; } } } void ResultIterator::MoveToLogicalStartOfTextline() { GenericVectorEqEq word_indices; RestartRow(); CalculateTextlineOrder(current_paragraph_is_ltr_, dynamic_cast(*this), &word_indices); int i = 0; for (; i < word_indices.size() && word_indices[i] < 0; i++) { if (word_indices[i] == kMinorRunStart) in_minor_direction_ = true; else if (word_indices[i] == kMinorRunEnd) in_minor_direction_ = false; } if (in_minor_direction_) at_beginning_of_minor_run_ = true; if (i >= word_indices.size()) return; int first_word_index = word_indices[i]; for (int j = 0; j < first_word_index; j++) { PageIterator::Next(RIL_WORD); } MoveToLogicalStartOfWord(); } void ResultIterator::Begin() { LTRResultIterator::Begin(); current_paragraph_is_ltr_ = CurrentParagraphIsLtr(); in_minor_direction_ = false; at_beginning_of_minor_run_ = false; MoveToLogicalStartOfTextline(); } bool ResultIterator::Next(PageIteratorLevel level) { if (it_->block() == NULL) return false; // already at end! switch (level) { case RIL_BLOCK: // explicit fall-through case RIL_PARA: // explicit fall-through case RIL_TEXTLINE: if (!PageIterator::Next(level)) return false; if (IsWithinFirstTextlineOfParagraph()) { // if we've advanced to a new paragraph, // recalculate current_paragraph_is_ltr_ current_paragraph_is_ltr_ = CurrentParagraphIsLtr(); } in_minor_direction_ = false; MoveToLogicalStartOfTextline(); return it_->block() != NULL; case RIL_SYMBOL: { GenericVector blob_order; CalculateBlobOrder(&blob_order); int next_blob = 0; while (next_blob < blob_order.size() && blob_index_ != blob_order[next_blob]) next_blob++; next_blob++; if (next_blob < blob_order.size()) { // we're in the same word; simply advance one blob. BeginWord(blob_order[next_blob]); at_beginning_of_minor_run_ = false; return true; } level = RIL_WORD; // we've fallen through to the next word. } case RIL_WORD: // explicit fall-through. { if (it_->word() == NULL) return Next(RIL_BLOCK); GenericVectorEqEq word_indices; int this_word_index = LTRWordIndex(); CalculateTextlineOrder(current_paragraph_is_ltr_, *this, &word_indices); int final_real_index = word_indices.size() - 1; while (final_real_index > 0 && word_indices[final_real_index] < 0) final_real_index--; for (int i = 0; i < final_real_index; i++) { if (word_indices[i] == this_word_index) { int j = i + 1; for (; j < final_real_index && word_indices[j] < 0; j++) { if (word_indices[j] == kMinorRunStart) in_minor_direction_ = true; if (word_indices[j] == kMinorRunEnd) in_minor_direction_ = false; } at_beginning_of_minor_run_ = (word_indices[j - 1] == kMinorRunStart); // awesome, we move to word_indices[j] if (BidiDebug(3)) { tprintf("Next(RIL_WORD): %d -> %d\n", this_word_index, word_indices[j]); } PageIterator::RestartRow(); for (int k = 0; k < word_indices[j]; k++) { PageIterator::Next(RIL_WORD); } MoveToLogicalStartOfWord(); return true; } } if (BidiDebug(3)) { tprintf("Next(RIL_WORD): %d -> EOL\n", this_word_index); } // we're going off the end of the text line. return Next(RIL_TEXTLINE); } } ASSERT_HOST(false); // shouldn't happen. return false; } bool ResultIterator::IsAtBeginningOf(PageIteratorLevel level) const { if (it_->block() == NULL) return false; // Already at the end! if (it_->word() == NULL) return true; // In an image block. if (level == RIL_SYMBOL) return true; // Always at beginning of a symbol. bool at_word_start = IsAtFirstSymbolOfWord(); if (level == RIL_WORD) return at_word_start; ResultIterator line_start(*this); // move to the first word in the line... line_start.MoveToLogicalStartOfTextline(); bool at_textline_start = at_word_start && *line_start.it_ == *it_; if (level == RIL_TEXTLINE) return at_textline_start; // now we move to the left-most word... line_start.RestartRow(); bool at_block_start = at_textline_start && line_start.it_->block() != line_start.it_->prev_block(); if (level == RIL_BLOCK) return at_block_start; bool at_para_start = at_block_start || (at_textline_start && line_start.it_->row()->row->para() != line_start.it_->prev_row()->row->para()); if (level == RIL_PARA) return at_para_start; ASSERT_HOST(false); // shouldn't happen. return false; } /** * NOTE! This is an exact copy of PageIterator::IsAtFinalElement with the * change that the variable next is now a ResultIterator instead of a * PageIterator. */ bool ResultIterator::IsAtFinalElement(PageIteratorLevel level, PageIteratorLevel element) const { if (Empty(element)) return true; // Already at the end! // The result is true if we step forward by element and find we are // at the the end of the page or at beginning of *all* levels in: // [level, element). // When there is more than one level difference between element and level, // we could for instance move forward one symbol and still be at the first // word on a line, so we also have to be at the first symbol in a word. ResultIterator next(*this); next.Next(element); if (next.Empty(element)) return true; // Reached the end of the page. while (element > level) { element = static_cast(element - 1); if (!next.IsAtBeginningOf(element)) return false; } return true; } /** * Returns the null terminated UTF-8 encoded text string for the current * object at the given level. Use delete [] to free after use. */ char* ResultIterator::GetUTF8Text(PageIteratorLevel level) const { if (it_->word() == NULL) return NULL; // Already at the end! STRING text; switch (level) { case RIL_BLOCK: { ResultIterator pp(*this); do { pp.AppendUTF8ParagraphText(&text); } while (pp.Next(RIL_PARA) && pp.it_->block() == it_->block()); } break; case RIL_PARA: AppendUTF8ParagraphText(&text); break; case RIL_TEXTLINE: { ResultIterator it(*this); it.MoveToLogicalStartOfTextline(); it.IterateAndAppendUTF8TextlineText(&text); } break; case RIL_WORD: AppendUTF8WordText(&text); break; case RIL_SYMBOL: { bool reading_direction_is_ltr = current_paragraph_is_ltr_ ^ in_minor_direction_; if (at_beginning_of_minor_run_) { text += reading_direction_is_ltr ? kLRM : kRLM; } text = it_->word()->BestUTF8(blob_index_, !reading_direction_is_ltr); if (IsAtFinalSymbolOfWord()) AppendSuffixMarks(&text); } break; } int length = text.length() + 1; char* result = new char[length]; strncpy(result, text.string(), length); return result; } void ResultIterator::AppendUTF8WordText(STRING *text) const { if (!it_->word()) return; ASSERT_HOST(it_->word()->best_choice != NULL); bool reading_direction_is_ltr = current_paragraph_is_ltr_ ^ in_minor_direction_; if (at_beginning_of_minor_run_) { *text += reading_direction_is_ltr ? kLRM : kRLM; } GenericVector blob_order; CalculateBlobOrder(&blob_order); for (int i = 0; i < blob_order.size(); i++) { *text += it_->word()->BestUTF8(blob_order[i], !reading_direction_is_ltr); } AppendSuffixMarks(text); } void ResultIterator::IterateAndAppendUTF8TextlineText(STRING *text) { if (Empty(RIL_WORD)) { Next(RIL_WORD); return; } if (BidiDebug(1)) { GenericVectorEqEq textline_order; GenericVector dirs; CalculateTextlineOrder(current_paragraph_is_ltr_, *this, &dirs, &textline_order); tprintf("Strong Script dirs [%p/P=%s]: ", it_->row(), current_paragraph_is_ltr_ ? "ltr" : "rtl"); PrintScriptDirs(dirs); tprintf("Logical textline order [%p/P=%s]: ", it_->row(), current_paragraph_is_ltr_ ? "ltr" : "rtl"); for (int i = 0; i < textline_order.size(); i++) { tprintf("%d ", textline_order[i]); } tprintf("\n"); } int words_appended = 0; do { int numSpaces = preserve_interword_spaces_ ? it_->word()->word->space() : (words_appended > 0); for (int i = 0; i < numSpaces; ++i) { *text += " "; } AppendUTF8WordText(text); words_appended++; } while (Next(RIL_WORD) && !IsAtBeginningOf(RIL_TEXTLINE)); if (BidiDebug(1)) { tprintf("%d words printed\n", words_appended); } *text += line_separator_; // If we just finished a paragraph, add an extra newline. if (it_->block() == NULL || IsAtBeginningOf(RIL_PARA)) *text += paragraph_separator_; } void ResultIterator::AppendUTF8ParagraphText(STRING *text) const { ResultIterator it(*this); it.RestartParagraph(); it.MoveToLogicalStartOfTextline(); if (it.Empty(RIL_WORD)) return; do { it.IterateAndAppendUTF8TextlineText(text); } while (it.it_->block() != NULL && !it.IsAtBeginningOf(RIL_PARA)); } bool ResultIterator::BidiDebug(int min_level) const { int debug_level = 1; IntParam *p = ParamUtils::FindParam( "bidi_debug", GlobalParams()->int_params, tesseract_->params()->int_params); if (p != NULL) debug_level = (inT32)(*p); return debug_level >= min_level; } } // namespace tesseract. tesseract-3.04.01/ccmain/resultiterator.h000066400000000000000000000215761266071204500203650ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: resultiterator.h // Description: Iterator for tesseract results that is capable of // iterating in proper reading order over Bi Directional // (e.g. mixed Hebrew and English) text. // Author: David Eger // Created: Fri May 27 13:58:06 PST 2011 // // (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_CCMAIN_RESULT_ITERATOR_H__ #define TESSERACT_CCMAIN_RESULT_ITERATOR_H__ #include "platform.h" #include "ltrresultiterator.h" template class GenericVector; template class GenericVectorEqEq; class BLOB_CHOICE_IT; class WERD_RES; class STRING; namespace tesseract { class Tesseract; class TESS_API ResultIterator : public LTRResultIterator { public: static ResultIterator *StartOfParagraph(const LTRResultIterator &resit); /** * ResultIterator is copy constructible! * The default copy constructor works just fine for us. */ virtual ~ResultIterator() {} // ============= Moving around within the page ============. /** * Moves the iterator to point to the start of the page to begin * an iteration. */ virtual void Begin(); /** * Moves to the start of the next object at the given level in the * page hierarchy in the appropriate reading order and returns false if * the end of the page was reached. * NOTE that RIL_SYMBOL will skip non-text blocks, but all other * PageIteratorLevel level values will visit each non-text block once. * Think of non text blocks as containing a single para, with a single line, * with a single imaginary word. * Calls to Next with different levels may be freely intermixed. * This function iterates words in right-to-left scripts correctly, if * the appropriate language has been loaded into Tesseract. */ virtual bool Next(PageIteratorLevel level); /** * IsAtBeginningOf() returns whether we're at the logical beginning of the * given level. (as opposed to ResultIterator's left-to-right top-to-bottom * order). Otherwise, this acts the same as PageIterator::IsAtBeginningOf(). * For a full description, see pageiterator.h */ virtual bool IsAtBeginningOf(PageIteratorLevel level) const; /** * Implement PageIterator's IsAtFinalElement correctly in a BiDi context. * For instance, IsAtFinalElement(RIL_PARA, RIL_WORD) returns whether we * point at the last word in a paragraph. See PageIterator for full comment. */ virtual bool IsAtFinalElement(PageIteratorLevel level, PageIteratorLevel element) const; // ============= Accessing data ==============. /** * Returns the null terminated UTF-8 encoded text string for the current * object at the given level. Use delete [] to free after use. */ virtual char* GetUTF8Text(PageIteratorLevel level) const; /** * Return whether the current paragraph's dominant reading direction * is left-to-right (as opposed to right-to-left). */ bool ParagraphIsLtr() const; // ============= Exposed only for testing =============. /** * Yields the reading order as a sequence of indices and (optional) * meta-marks for a set of words (given left-to-right). * The meta marks are passed as negative values: * kMinorRunStart Start of minor direction text. * kMinorRunEnd End of minor direction text. * kComplexWord The next indexed word contains both left-to-right and * right-to-left characters and was treated as neutral. * * For example, suppose we have five words in a text line, * indexed [0,1,2,3,4] from the leftmost side of the text line. * The following are all believable reading_orders: * * Left-to-Right (in ltr paragraph): * { 0, 1, 2, 3, 4 } * Left-to-Right (in rtl paragraph): * { kMinorRunStart, 0, 1, 2, 3, 4, kMinorRunEnd } * Right-to-Left (in rtl paragraph): * { 4, 3, 2, 1, 0 } * Left-to-Right except for an RTL phrase in words 2, 3 in an ltr paragraph: * { 0, 1, kMinorRunStart, 3, 2, kMinorRunEnd, 4 } */ static void CalculateTextlineOrder( bool paragraph_is_ltr, const GenericVector &word_dirs, GenericVectorEqEq *reading_order); static const int kMinorRunStart; static const int kMinorRunEnd; static const int kComplexWord; protected: /** * We presume the data associated with the given iterator will outlive us. * NB: This is private because it does something that is non-obvious: * it resets to the beginning of the paragraph instead of staying wherever * resit might have pointed. */ TESS_LOCAL explicit ResultIterator(const LTRResultIterator &resit); private: /** * Calculates the current paragraph's dominant writing direction. * Typically, members should use current_paragraph_ltr_ instead. */ bool CurrentParagraphIsLtr() const; /** * Returns word indices as measured from resit->RestartRow() = index 0 * for the reading order of words within a textline given an iterator * into the middle of the text line. * In addition to non-negative word indices, the following negative values * may be inserted: * kMinorRunStart Start of minor direction text. * kMinorRunEnd End of minor direction text. * kComplexWord The previous word contains both left-to-right and * right-to-left characters and was treated as neutral. */ void CalculateTextlineOrder(bool paragraph_is_ltr, const LTRResultIterator &resit, GenericVectorEqEq *indices) const; /** Same as above, but the caller's ssd gets filled in if ssd != NULL. */ void CalculateTextlineOrder(bool paragraph_is_ltr, const LTRResultIterator &resit, GenericVector *ssd, GenericVectorEqEq *indices) const; /** * What is the index of the current word in a strict left-to-right reading * of the row? */ int LTRWordIndex() const; /** * Given an iterator pointing at a word, returns the logical reading order * of blob indices for the word. */ void CalculateBlobOrder(GenericVector *blob_indices) const; /** Precondition: current_paragraph_is_ltr_ is set. */ void MoveToLogicalStartOfTextline(); /** * Precondition: current_paragraph_is_ltr_ and in_minor_direction_ * are set. */ void MoveToLogicalStartOfWord(); /** Are we pointing at the final (reading order) symbol of the word? */ bool IsAtFinalSymbolOfWord() const; /** Are we pointing at the first (reading order) symbol of the word? */ bool IsAtFirstSymbolOfWord() const; /** * Append any extra marks that should be appended to this word when printed. * Mostly, these are Unicode BiDi control characters. */ void AppendSuffixMarks(STRING *text) const; /** Appends the current word in reading order to the given buffer.*/ void AppendUTF8WordText(STRING *text) const; /** * Appends the text of the current text line, *assuming this iterator is * positioned at the beginning of the text line* This function * updates the iterator to point to the first position past the text line. * Each textline is terminated in a single newline character. * If the textline ends a paragraph, it gets a second terminal newline. */ void IterateAndAppendUTF8TextlineText(STRING *text); /** * Appends the text of the current paragraph in reading order * to the given buffer. * Each textline is terminated in a single newline character, and the * paragraph gets an extra newline at the end. */ void AppendUTF8ParagraphText(STRING *text) const; /** Returns whether the bidi_debug flag is set to at least min_level. */ bool BidiDebug(int min_level) const; bool current_paragraph_is_ltr_; /** * Is the currently pointed-at character at the beginning of * a minor-direction run? */ bool at_beginning_of_minor_run_; /** Is the currently pointed-at character in a minor-direction sequence? */ bool in_minor_direction_; /** * Should detected inter-word spaces be preserved, or "compressed" to a single * space character (default behavior). */ bool preserve_interword_spaces_; }; } // namespace tesseract. #endif // TESSERACT_CCMAIN_RESULT_ITERATOR_H__ tesseract-3.04.01/ccmain/superscript.cpp000066400000000000000000000563631266071204500202150ustar00rootroot00000000000000/****************************************************************** * File: superscript.cpp * Description: Correction pass to fix superscripts and subscripts. * Author: David Eger * Created: Mon Mar 12 14:05: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 "normalis.h" #include "tesseractclass.h" static int LeadingUnicharsToChopped(WERD_RES *word, int num_unichars) { int num_chopped = 0; for (int i = 0; i < num_unichars; i++) num_chopped += word->best_state[i]; return num_chopped; } static int TrailingUnicharsToChopped(WERD_RES *word, int num_unichars) { int num_chopped = 0; for (int i = 0; i < num_unichars; i++) num_chopped += word->best_state[word->best_state.size() - 1 - i]; return num_chopped; } namespace tesseract { /** * Given a recognized blob, see if a contiguous collection of sub-pieces * (chopped blobs) starting at its left might qualify as being a subscript * or superscript letter based only on y position. Also do this for the * right side. */ void YOutlierPieces(WERD_RES *word, int rebuilt_blob_index, int super_y_bottom, int sub_y_top, ScriptPos *leading_pos, int *num_leading_outliers, ScriptPos *trailing_pos, int *num_trailing_outliers) { ScriptPos sp_unused1, sp_unused2; int unused1, unused2; if (!leading_pos) leading_pos = &sp_unused1; if (!num_leading_outliers) num_leading_outliers = &unused1; if (!trailing_pos) trailing_pos = &sp_unused2; if (!num_trailing_outliers) num_trailing_outliers = &unused2; *num_leading_outliers = *num_trailing_outliers = 0; *leading_pos = *trailing_pos = SP_NORMAL; int chopped_start = LeadingUnicharsToChopped(word, rebuilt_blob_index); int num_chopped_pieces = word->best_state[rebuilt_blob_index]; ScriptPos last_pos = SP_NORMAL; int trailing_outliers = 0; for (int i = 0; i < num_chopped_pieces; i++) { TBOX box = word->chopped_word->blobs[chopped_start + i]->bounding_box(); ScriptPos pos = SP_NORMAL; if (box.bottom() >= super_y_bottom) { pos = SP_SUPERSCRIPT; } else if (box.top() <= sub_y_top) { pos = SP_SUBSCRIPT; } if (pos == SP_NORMAL) { if (trailing_outliers == i) { *num_leading_outliers = trailing_outliers; *leading_pos = last_pos; } trailing_outliers = 0; } else { if (pos == last_pos) { trailing_outliers++; } else { trailing_outliers = 1; } } last_pos = pos; } *num_trailing_outliers = trailing_outliers; *trailing_pos = last_pos; } /** * Attempt to split off any high (or low) bits at the ends of the word with poor * certainty and recognize them separately. If the certainty gets much better * and other sanity checks pass, acccept. * * This superscript fix is meant to be called in the second pass of recognition * when we have tried once and already have a preliminary answer for word. * * @return Whether we modified the given word. */ bool Tesseract::SubAndSuperscriptFix(WERD_RES *word) { if (word->tess_failed || word->word->flag(W_REP_CHAR) || !word->best_choice) { return false; } int num_leading, num_trailing; ScriptPos sp_leading, sp_trailing; float leading_certainty, trailing_certainty; float avg_certainty, unlikely_threshold; // Calculate the number of whole suspicious characters at the edges. GetSubAndSuperscriptCandidates( word, &num_leading, &sp_leading, &leading_certainty, &num_trailing, &sp_trailing, &trailing_certainty, &avg_certainty, &unlikely_threshold); const char *leading_pos = sp_leading == SP_SUBSCRIPT ? "sub" : "super"; const char *trailing_pos = sp_trailing == SP_SUBSCRIPT ? "sub" : "super"; int num_blobs = word->best_choice->length(); // Calculate the remainder (partial characters) at the edges. // This accounts for us having classified the best version of // a word as [speaker?'] when it was instead [speaker.^{21}] // (that is we accidentally thought the 2 was attached to the period). int num_remainder_leading = 0, num_remainder_trailing = 0; if (num_leading + num_trailing < num_blobs && unlikely_threshold < 0.0) { int super_y_bottom = kBlnBaselineOffset + kBlnXHeight * superscript_min_y_bottom; int sub_y_top = kBlnBaselineOffset + kBlnXHeight * subscript_max_y_top; int last_word_char = num_blobs - 1 - num_trailing; float last_char_certainty = word->best_choice->certainty(last_word_char); if (word->best_choice->unichar_id(last_word_char) != 0 && last_char_certainty <= unlikely_threshold) { ScriptPos rpos; YOutlierPieces(word, last_word_char, super_y_bottom, sub_y_top, NULL, NULL, &rpos, &num_remainder_trailing); if (num_trailing > 0 && rpos != sp_trailing) num_remainder_trailing = 0; if (num_remainder_trailing > 0 && last_char_certainty < trailing_certainty) { trailing_certainty = last_char_certainty; } } bool another_blob_available = (num_remainder_trailing == 0) || num_leading + num_trailing + 1 < num_blobs; int first_char_certainty = word->best_choice->certainty(num_leading); if (another_blob_available && word->best_choice->unichar_id(num_leading) != 0 && first_char_certainty <= unlikely_threshold) { ScriptPos lpos; YOutlierPieces(word, num_leading, super_y_bottom, sub_y_top, &lpos, &num_remainder_leading, NULL, NULL); if (num_leading > 0 && lpos != sp_leading) num_remainder_leading = 0; if (num_remainder_leading > 0 && first_char_certainty < leading_certainty) { leading_certainty = first_char_certainty; } } } // If nothing to do, bail now. if (num_leading + num_trailing + num_remainder_leading + num_remainder_trailing == 0) { return false; } if (superscript_debug >= 1) { tprintf("Candidate for superscript detection: %s (", word->best_choice->unichar_string().string()); if (num_leading || num_remainder_leading) { tprintf("%d.%d %s-leading ", num_leading, num_remainder_leading, leading_pos); } if (num_trailing || num_remainder_trailing) { tprintf("%d.%d %s-trailing ", num_trailing, num_remainder_trailing, trailing_pos); } tprintf(")\n"); } if (superscript_debug >= 3) { word->best_choice->print(); } if (superscript_debug >= 2) { tprintf(" Certainties -- Average: %.2f Unlikely thresh: %.2f ", avg_certainty, unlikely_threshold); if (num_leading) tprintf("Orig. leading (min): %.2f ", leading_certainty); if (num_trailing) tprintf("Orig. trailing (min): %.2f ", trailing_certainty); tprintf("\n"); } // We've now calculated the number of rebuilt blobs we want to carve off. // However, split_word() works from TBLOBs in chopped_word, so we need to // convert to those. int num_chopped_leading = LeadingUnicharsToChopped(word, num_leading) + num_remainder_leading; int num_chopped_trailing = TrailingUnicharsToChopped(word, num_trailing) + num_remainder_trailing; int retry_leading = 0; int retry_trailing = 0; bool is_good = false; WERD_RES *revised = TrySuperscriptSplits( num_chopped_leading, leading_certainty, sp_leading, num_chopped_trailing, trailing_certainty, sp_trailing, word, &is_good, &retry_leading, &retry_trailing); if (is_good) { word->ConsumeWordResults(revised); } else if (retry_leading || retry_trailing) { int retry_chopped_leading = LeadingUnicharsToChopped(revised, retry_leading); int retry_chopped_trailing = TrailingUnicharsToChopped(revised, retry_trailing); WERD_RES *revised2 = TrySuperscriptSplits( retry_chopped_leading, leading_certainty, sp_leading, retry_chopped_trailing, trailing_certainty, sp_trailing, revised, &is_good, &retry_leading, &retry_trailing); if (is_good) { word->ConsumeWordResults(revised2); } delete revised2; } delete revised; return is_good; } /** * Determine how many characters (rebuilt blobs) on each end of a given word * might plausibly be superscripts so SubAndSuperscriptFix can try to * re-recognize them. Even if we find no whole blobs at either end, * we will set *unlikely_threshold to a certainty that might be used to * select "bad enough" outlier characters. If *unlikely_threshold is set to 0, * though, there's really no hope. * * @param[in] word The word to examine. * @param[out] num_rebuilt_leading the number of rebuilt blobs at the start * of the word which are all up or down and * seem badly classified. * @param[out] leading_pos "super" or "sub" (for debugging) * @param[out] leading_certainty the worst certainty in the leading blobs. * @param[out] num_rebuilt_trailing the number of rebuilt blobs at the end * of the word which are all up or down and * seem badly classified. * @param[out] trailing_pos "super" or "sub" (for debugging) * @param[out] trailing_certainty the worst certainty in the trailing blobs. * @param[out] avg_certainty the average certainty of "normal" blobs in * the word. * @param[out] unlikely_threshold the threshold (on certainty) we used to * select "bad enough" outlier characters. */ void Tesseract::GetSubAndSuperscriptCandidates(const WERD_RES *word, int *num_rebuilt_leading, ScriptPos *leading_pos, float *leading_certainty, int *num_rebuilt_trailing, ScriptPos *trailing_pos, float *trailing_certainty, float *avg_certainty, float *unlikely_threshold) { *avg_certainty = *unlikely_threshold = 0.0f; *num_rebuilt_leading = *num_rebuilt_trailing = 0; *leading_certainty = *trailing_certainty = 0.0f; int super_y_bottom = kBlnBaselineOffset + kBlnXHeight * superscript_min_y_bottom; int sub_y_top = kBlnBaselineOffset + kBlnXHeight * subscript_max_y_top; // Step one: Get an average certainty for "normally placed" characters. // Counts here are of blobs in the rebuild_word / unichars in best_choice. *leading_pos = *trailing_pos = SP_NORMAL; int leading_outliers = 0; int trailing_outliers = 0; int num_normal = 0; float normal_certainty_total = 0.0f; float worst_normal_certainty = 0.0f; ScriptPos last_pos = SP_NORMAL; int num_blobs = word->rebuild_word->NumBlobs(); for (int b = 0; b < num_blobs; ++b) { TBOX box = word->rebuild_word->blobs[b]->bounding_box(); ScriptPos pos = SP_NORMAL; if (box.bottom() >= super_y_bottom) { pos = SP_SUPERSCRIPT; } else if (box.top() <= sub_y_top) { pos = SP_SUBSCRIPT; } if (pos == SP_NORMAL) { if (word->best_choice->unichar_id(b) != 0) { float char_certainty = word->best_choice->certainty(b); if (char_certainty < worst_normal_certainty) { worst_normal_certainty = char_certainty; } num_normal++; normal_certainty_total += char_certainty; } if (trailing_outliers == b) { leading_outliers = trailing_outliers; *leading_pos = last_pos; } trailing_outliers = 0; } else { if (last_pos == pos) { trailing_outliers++; } else { trailing_outliers = 1; } } last_pos = pos; } *trailing_pos = last_pos; if (num_normal >= 3) { // throw out the worst as an outlier. num_normal--; normal_certainty_total -= worst_normal_certainty; } if (num_normal > 0) { *avg_certainty = normal_certainty_total / num_normal; *unlikely_threshold = superscript_worse_certainty * (*avg_certainty); } if (num_normal == 0 || (leading_outliers == 0 && trailing_outliers == 0)) { return; } // Step two: Try to split off bits of the word that are both outliers // and have much lower certainty than average // Calculate num_leading and leading_certainty. for (*leading_certainty = 0.0f, *num_rebuilt_leading = 0; *num_rebuilt_leading < leading_outliers; (*num_rebuilt_leading)++) { float char_certainty = word->best_choice->certainty(*num_rebuilt_leading); if (char_certainty > *unlikely_threshold) { break; } if (char_certainty < *leading_certainty) { *leading_certainty = char_certainty; } } // Calculate num_trailing and trailing_certainty. for (*trailing_certainty = 0.0f, *num_rebuilt_trailing = 0; *num_rebuilt_trailing < trailing_outliers; (*num_rebuilt_trailing)++) { int blob_idx = num_blobs - 1 - *num_rebuilt_trailing; float char_certainty = word->best_choice->certainty(blob_idx); if (char_certainty > *unlikely_threshold) { break; } if (char_certainty < *trailing_certainty) { *trailing_certainty = char_certainty; } } } /** * Try splitting off the given number of (chopped) blobs from the front and * back of the given word and recognizing the pieces. * * @param[in] num_chopped_leading how many chopped blobs from the left * end of the word to chop off and try recognizing as a * superscript (or subscript) * @param[in] leading_certainty the (minimum) certainty had by the * characters in the original leading section. * @param[in] leading_pos "super" or "sub" (for debugging) * @param[in] num_chopped_trailing how many chopped blobs from the right * end of the word to chop off and try recognizing as a * superscript (or subscript) * @param[in] trailing_certainty the (minimum) certainty had by the * characters in the original trailing section. * @param[in] trailing_pos "super" or "sub" (for debugging) * @param[in] word the word to try to chop up. * @param[out] is_good do we believe our result? * @param[out] retry_rebuild_leading, retry_rebuild_trailing * If non-zero, and !is_good, then the caller may have luck trying * to split the returned word with this number of (rebuilt) leading * and trailing blobs / unichars. * @return A word which is the result of re-recognizing as asked. */ WERD_RES *Tesseract::TrySuperscriptSplits( int num_chopped_leading, float leading_certainty, ScriptPos leading_pos, int num_chopped_trailing, float trailing_certainty, ScriptPos trailing_pos, WERD_RES *word, bool *is_good, int *retry_rebuild_leading, int *retry_rebuild_trailing) { int num_chopped = word->chopped_word->NumBlobs(); *retry_rebuild_leading = *retry_rebuild_trailing = 0; // Chop apart the word into up to three pieces. BlamerBundle *bb0 = NULL; BlamerBundle *bb1 = NULL; WERD_RES *prefix = NULL; WERD_RES *core = NULL; WERD_RES *suffix = NULL; if (num_chopped_leading > 0) { prefix = new WERD_RES(*word); split_word(prefix, num_chopped_leading, &core, &bb0); } else { core = new WERD_RES(*word); } if (num_chopped_trailing > 0) { int split_pt = num_chopped - num_chopped_trailing - num_chopped_leading; split_word(core, split_pt, &suffix, &bb1); } // Recognize the pieces in turn. int saved_cp_multiplier = classify_class_pruner_multiplier; int saved_im_multiplier = classify_integer_matcher_multiplier; if (prefix) { // Turn off Tesseract's y-position penalties for the leading superscript. classify_class_pruner_multiplier.set_value(0); classify_integer_matcher_multiplier.set_value(0); // Adjust our expectations about the baseline for this prefix. if (superscript_debug >= 3) { tprintf(" recognizing first %d chopped blobs\n", num_chopped_leading); } recog_word_recursive(prefix); if (superscript_debug >= 2) { tprintf(" The leading bits look like %s %s\n", ScriptPosToString(leading_pos), prefix->best_choice->unichar_string().string()); } // Restore the normal y-position penalties. classify_class_pruner_multiplier.set_value(saved_cp_multiplier); classify_integer_matcher_multiplier.set_value(saved_im_multiplier); } if (superscript_debug >= 3) { tprintf(" recognizing middle %d chopped blobs\n", num_chopped - num_chopped_leading - num_chopped_trailing); } if (suffix) { // Turn off Tesseract's y-position penalties for the trailing superscript. classify_class_pruner_multiplier.set_value(0); classify_integer_matcher_multiplier.set_value(0); if (superscript_debug >= 3) { tprintf(" recognizing last %d chopped blobs\n", num_chopped_trailing); } recog_word_recursive(suffix); if (superscript_debug >= 2) { tprintf(" The trailing bits look like %s %s\n", ScriptPosToString(trailing_pos), suffix->best_choice->unichar_string().string()); } // Restore the normal y-position penalties. classify_class_pruner_multiplier.set_value(saved_cp_multiplier); classify_integer_matcher_multiplier.set_value(saved_im_multiplier); } // Evaluate whether we think the results are believably better // than what we already had. bool good_prefix = !prefix || BelievableSuperscript( superscript_debug >= 1, *prefix, superscript_bettered_certainty * leading_certainty, retry_rebuild_leading, NULL); bool good_suffix = !suffix || BelievableSuperscript( superscript_debug >= 1, *suffix, superscript_bettered_certainty * trailing_certainty, NULL, retry_rebuild_trailing); *is_good = good_prefix && good_suffix; if (!*is_good && !*retry_rebuild_leading && !*retry_rebuild_trailing) { // None of it is any good. Quit now. delete core; delete prefix; delete suffix; return NULL; } recog_word_recursive(core); // Now paste the results together into core. if (suffix) { suffix->SetAllScriptPositions(trailing_pos); join_words(core, suffix, bb1); } if (prefix) { prefix->SetAllScriptPositions(leading_pos); join_words(prefix, core, bb0); core = prefix; prefix = NULL; } if (superscript_debug >= 1) { tprintf("%s superscript fix: %s\n", *is_good ? "ACCEPT" : "REJECT", core->best_choice->unichar_string().string()); } return core; } /** * Return whether this is believable superscript or subscript text. * * We insist that: * + there are no punctuation marks. * + there are no italics. * + no normal-sized character is smaller than superscript_scaledown_ratio * of what it ought to be, and * + each character is at least as certain as certainty_threshold. * * @param[in] debug If true, spew debug output * @param[in] word The word whose best_choice we're evaluating * @param[in] certainty_threshold If any of the characters have less * certainty than this, reject. * @param[out] left_ok How many left-side characters were ok? * @param[out] right_ok How many right-side characters were ok? * @return Whether the complete best choice is believable as a superscript. */ bool Tesseract::BelievableSuperscript(bool debug, const WERD_RES &word, float certainty_threshold, int *left_ok, int *right_ok) const { int initial_ok_run_count = 0; int ok_run_count = 0; float worst_certainty = 0.0f; const WERD_CHOICE &wc = *word.best_choice; const UnicityTable& fontinfo_table = get_fontinfo_table(); for (int i = 0; i < wc.length(); i++) { TBLOB *blob = word.rebuild_word->blobs[i]; UNICHAR_ID unichar_id = wc.unichar_id(i); float char_certainty = wc.certainty(i); bool bad_certainty = char_certainty < certainty_threshold; bool is_punc = wc.unicharset()->get_ispunctuation(unichar_id); bool is_italic = word.fontinfo && word.fontinfo->is_italic(); BLOB_CHOICE *choice = word.GetBlobChoice(i); if (choice && fontinfo_table.size() > 0) { // Get better information from the specific choice, if available. int font_id1 = choice->fontinfo_id(); bool font1_is_italic = font_id1 >= 0 ? fontinfo_table.get(font_id1).is_italic() : false; int font_id2 = choice->fontinfo_id2(); is_italic = font1_is_italic && (font_id2 < 0 || fontinfo_table.get(font_id2).is_italic()); } float height_fraction = 1.0f; float char_height = blob->bounding_box().height(); float normal_height = char_height; if (wc.unicharset()->top_bottom_useful()) { int min_bot, max_bot, min_top, max_top; wc.unicharset()->get_top_bottom(unichar_id, &min_bot, &max_bot, &min_top, &max_top); float hi_height = max_top - max_bot; float lo_height = min_top - min_bot; normal_height = (hi_height + lo_height) / 2; if (normal_height >= kBlnXHeight) { // Only ding characters that we have decent information for because // they're supposed to be normal sized, not tiny specks or dashes. height_fraction = char_height / normal_height; } } bool bad_height = height_fraction < superscript_scaledown_ratio; if (debug) { if (is_italic) { tprintf(" Rejecting: superscript is italic.\n"); } if (is_punc) { tprintf(" Rejecting: punctuation present.\n"); } const char *char_str = wc.unicharset()->id_to_unichar(unichar_id); if (bad_certainty) { tprintf(" Rejecting: don't believe character %s with certainty %.2f " "which is less than threshold %.2f\n", char_str, char_certainty, certainty_threshold); } if (bad_height) { tprintf(" Rejecting: character %s seems too small @ %.2f versus " "expected %.2f\n", char_str, char_height, normal_height); } } if (bad_certainty || bad_height || is_punc || is_italic) { if (ok_run_count == i) { initial_ok_run_count = ok_run_count; } ok_run_count = 0; } else { ok_run_count++; } if (char_certainty < worst_certainty) { worst_certainty = char_certainty; } } bool all_ok = ok_run_count == wc.length(); if (all_ok && debug) { tprintf(" Accept: worst revised certainty is %.2f\n", worst_certainty); } if (!all_ok) { if (left_ok) *left_ok = initial_ok_run_count; if (right_ok) *right_ok = ok_run_count; } return all_ok; } } // namespace tesseract tesseract-3.04.01/ccmain/tessbox.cpp000066400000000000000000000045371266071204500173150ustar00rootroot00000000000000/********************************************************************** * File: tessbox.cpp (Formerly tessbox.c) * Description: Black boxed Tess for developing a resaljet. * Author: Ray Smith * Created: Thu Apr 23 11:03:36 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. * **********************************************************************/ #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #endif #include "mfoutline.h" #include "tessbox.h" #include "tesseractclass.h" #define EXTERN /** * @name tess_segment_pass_n * * Segment a word using the pass_n conditions of the tess segmenter. * @param pass_n pass number * @param word word to do */ namespace tesseract { void Tesseract::tess_segment_pass_n(int pass_n, WERD_RES *word) { 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 (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); } } /** * @name tess_acceptable_word * * @return true if the word is regarded as "good enough". * @param word_choice after context * @param raw_choice before context */ bool Tesseract::tess_acceptable_word(WERD_RES* word) { return getDict().AcceptableResult(word); } /** * @name tess_add_doc_word * * Add the given word to the document dictionary */ void Tesseract::tess_add_doc_word(WERD_CHOICE *word_choice) { getDict().add_document_word(*word_choice); } } // namespace tesseract tesseract-3.04.01/ccmain/tessbox.h000066400000000000000000000021371266071204500167540ustar00rootroot00000000000000/********************************************************************** * File: tessbox.h (Formerly tessbox.h) * Description: Black boxed Tess for developing a resaljet. * Author: Ray Smith * Created: Thu Apr 23 11:03:36 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 TESSBOX_H #define TESSBOX_H #include "ratngs.h" #include "tesseractclass.h" // TODO(ocr-team): Delete this along with other empty header files. #endif tesseract-3.04.01/ccmain/tessedit.cpp000066400000000000000000000447461266071204500174600ustar00rootroot00000000000000/********************************************************************** * File: tessedit.cpp (Formerly tessedit.c) * Description: (Previously) Main program for merge of tess and editor. * Now just code to load the language model and various * engine-specific data files. * Author: Ray Smith * Created: Tue Jan 07 15:21:46 GMT 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. * **********************************************************************/ // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "stderr.h" #include "basedir.h" #include "tessvars.h" #include "control.h" #include "reject.h" #include "pageres.h" #include "nwmain.h" #include "pgedit.h" #include "tprintf.h" #include "tessedit.h" #include "stopper.h" #include "intmatcher.h" #include "chop.h" #include "efio.h" #include "danerror.h" #include "globals.h" #include "tesseractclass.h" #include "params.h" #define VARDIR "configs/" /*variables files */ //config under api #define API_CONFIG "configs/api_config" ETEXT_DESC *global_monitor = NULL; // progress monitor namespace tesseract { // Read a "config" file containing a set of variable, value pairs. // Searches the standard places: tessdata/configs, tessdata/tessconfigs // and also accepts a relative or absolute path name. void Tesseract::read_config_file(const char *filename, SetParamConstraint constraint) { STRING path = datadir; path += "configs/"; path += filename; FILE* fp; if ((fp = fopen(path.string(), "rb")) != NULL) { fclose(fp); } else { path = datadir; path += "tessconfigs/"; path += filename; if ((fp = fopen(path.string(), "rb")) != NULL) { fclose(fp); } else { path = filename; } } ParamUtils::ReadParamsFile(path.string(), constraint, this->params()); } // Returns false if a unicharset file for the specified language was not found // or was invalid. // This function initializes TessdataManager. After TessdataManager is // no longer needed, TessdataManager::End() should be called. // // This function sets tessedit_oem_mode to the given OcrEngineMode oem, unless // it is OEM_DEFAULT, in which case the value of the variable will be obtained // from the language-specific config file (stored in [lang].traineddata), from // the config files specified on the command line or left as the default // OEM_TESSERACT_ONLY if none of the configs specify this variable. bool Tesseract::init_tesseract_lang_data( const char *arg0, const char *textbase, const char *language, OcrEngineMode oem, char **configs, int configs_size, const GenericVector *vars_vec, const GenericVector *vars_values, bool set_only_non_debug_params) { // Set the basename, compute the data directory. main_setup(arg0, textbase); // Set the language data path prefix lang = language != NULL ? language : "eng"; language_data_path_prefix = datadir; language_data_path_prefix += lang; language_data_path_prefix += "."; // Initialize TessdataManager. STRING tessdata_path = language_data_path_prefix + kTrainedDataSuffix; if (!tessdata_manager.Init(tessdata_path.string(), tessdata_manager_debug_level)) { return false; } // If a language specific config file (lang.config) exists, load it in. if (tessdata_manager.SeekToStart(TESSDATA_LANG_CONFIG)) { ParamUtils::ReadParamsFromFp( tessdata_manager.GetDataFilePtr(), tessdata_manager.GetEndOffset(TESSDATA_LANG_CONFIG), SET_PARAM_CONSTRAINT_NONE, this->params()); if (tessdata_manager_debug_level) { tprintf("Loaded language config file\n"); } } SetParamConstraint set_params_constraint = set_only_non_debug_params ? SET_PARAM_CONSTRAINT_NON_DEBUG_ONLY : SET_PARAM_CONSTRAINT_NONE; // Load tesseract variables from config files. This is done after loading // language-specific variables from [lang].traineddata file, so that custom // config files can override values in [lang].traineddata file. for (int i = 0; i < configs_size; ++i) { read_config_file(configs[i], set_params_constraint); } // Set params specified in vars_vec (done after setting params from config // files, so that params in vars_vec can override those from files). if (vars_vec != NULL && vars_values != NULL) { for (int i = 0; i < vars_vec->size(); ++i) { if (!ParamUtils::SetParam((*vars_vec)[i].string(), (*vars_values)[i].string(), set_params_constraint, this->params())) { tprintf("Error setting param %s\n", (*vars_vec)[i].string()); exit(1); } } } if (((STRING &)tessedit_write_params_to_file).length() > 0) { FILE *params_file = fopen(tessedit_write_params_to_file.string(), "wb"); if (params_file != NULL) { ParamUtils::PrintParams(params_file, this->params()); fclose(params_file); if (tessdata_manager_debug_level > 0) { tprintf("Wrote parameters to %s\n", tessedit_write_params_to_file.string()); } } else { tprintf("Failed to open %s for writing params.\n", tessedit_write_params_to_file.string()); } } // Determine which ocr engine(s) should be loaded and used for recognition. if (oem != OEM_DEFAULT) tessedit_ocr_engine_mode.set_value(oem); if (tessdata_manager_debug_level) { tprintf("Loading Tesseract/Cube with tessedit_ocr_engine_mode %d\n", static_cast(tessedit_ocr_engine_mode)); } // If we are only loading the config file (and so not planning on doing any // recognition) then there's nothing else do here. if (tessedit_init_config_only) { if (tessdata_manager_debug_level) { tprintf("Returning after loading config file\n"); } return true; } // Load the unicharset if (!tessdata_manager.SeekToStart(TESSDATA_UNICHARSET) || !unicharset.load_from_file(tessdata_manager.GetDataFilePtr())) { return false; } if (unicharset.size() > MAX_NUM_CLASSES) { tprintf("Error: Size of unicharset is greater than MAX_NUM_CLASSES\n"); return false; } 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)) { TFile ambigs_file; ambigs_file.Open(tessdata_manager.GetDataFilePtr(), tessdata_manager.GetEndOffset(TESSDATA_AMBIGS) + 1); unichar_ambigs.LoadUnicharAmbigs( encoder_unicharset, &ambigs_file, ambigs_debug_level, use_ambigs_for_adaption, &unicharset); if (tessdata_manager_debug_level) tprintf("Loaded ambigs\n"); } // The various OcrEngineMode settings (see publictypes.h) determine which // engine-specific data files need to be loaded. Currently everything needs // the base tesseract data, which supplies other useful information, but // alternative engines, such as cube and LSTM are optional. #ifndef NO_CUBE_BUILD if (tessedit_ocr_engine_mode == OEM_CUBE_ONLY) { ASSERT_HOST(init_cube_objects(false, &tessdata_manager)); if (tessdata_manager_debug_level) tprintf("Loaded Cube w/out combiner\n"); } else if (tessedit_ocr_engine_mode == OEM_TESSERACT_CUBE_COMBINED) { ASSERT_HOST(init_cube_objects(true, &tessdata_manager)); if (tessdata_manager_debug_level) tprintf("Loaded Cube with combiner\n"); } #endif // 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; } // Helper returns true if the given string is in the vector of strings. static bool IsStrInList(const STRING& str, const GenericVector& str_list) { for (int i = 0; i < str_list.size(); ++i) { if (str_list[i] == str) return true; } return false; } // Parse a string of the form [~][+[~]]*. // Langs with no prefix get appended to to_load, provided they // are not in there already. // Langs with ~ prefix get appended to not_to_load, provided they are not in // there already. void Tesseract::ParseLanguageString(const char* lang_str, GenericVector* to_load, GenericVector* not_to_load) { STRING remains(lang_str); while (remains.length() > 0) { // Find the start of the lang code and which vector to add to. const char* start = remains.string(); while (*start == '+') ++start; GenericVector* target = to_load; if (*start == '~') { target = not_to_load; ++start; } // Find the index of the end of the lang code in string start. int end = strlen(start); const char* plus = strchr(start, '+'); if (plus != NULL && plus - start < end) end = plus - start; STRING lang_code(start); lang_code.truncate_at(end); STRING next(start + end); remains = next; // Check whether lang_code is already in the target vector and add. if (!IsStrInList(lang_code, *target)) { if (tessdata_manager_debug_level) tprintf("Adding language '%s' to list\n", lang_code.string()); target->push_back(lang_code); } } } // Initialize for potentially a set of languages defined by the language // string and recursively any additional languages required by any language // traineddata file (via tessedit_load_sublangs in its config) that is loaded. // See init_tesseract_internal for args. int Tesseract::init_tesseract( const char *arg0, const char *textbase, const char *language, OcrEngineMode oem, char **configs, int configs_size, const GenericVector *vars_vec, const GenericVector *vars_values, bool set_only_non_debug_params) { GenericVector langs_to_load; GenericVector langs_not_to_load; ParseLanguageString(language, &langs_to_load, &langs_not_to_load); sub_langs_.delete_data_pointers(); sub_langs_.clear(); // Find the first loadable lang and load into this. // Add any languages that this language requires bool loaded_primary = false; // Load the rest into sub_langs_. for (int lang_index = 0; lang_index < langs_to_load.size(); ++lang_index) { if (!IsStrInList(langs_to_load[lang_index], langs_not_to_load)) { const char *lang_str = langs_to_load[lang_index].string(); Tesseract *tess_to_init; if (!loaded_primary) { tess_to_init = this; } else { tess_to_init = new Tesseract; } int result = tess_to_init->init_tesseract_internal( arg0, textbase, lang_str, oem, configs, configs_size, vars_vec, vars_values, set_only_non_debug_params); if (!loaded_primary) { if (result < 0) { tprintf("Failed loading language '%s'\n", lang_str); } else { if (tessdata_manager_debug_level) tprintf("Loaded language '%s' as main language\n", lang_str); ParseLanguageString(tess_to_init->tessedit_load_sublangs.string(), &langs_to_load, &langs_not_to_load); loaded_primary = true; } } else { if (result < 0) { tprintf("Failed loading language '%s'\n", lang_str); delete tess_to_init; } else { if (tessdata_manager_debug_level) tprintf("Loaded language '%s' as secondary language\n", lang_str); sub_langs_.push_back(tess_to_init); // Add any languages that this language requires ParseLanguageString(tess_to_init->tessedit_load_sublangs.string(), &langs_to_load, &langs_not_to_load); } } } } if (!loaded_primary) { 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(); } if (tessdata_manager_debug_level) tprintf("Using default language params\n"); } } SetupUniversalFontIds(); return 0; } // Common initialization for a single language. // arg0 is the datapath for the tessdata directory, which could be the // path of the tessdata directory with no trailing /, or (if tessdata // lives in the same directory as the executable, the path of the executable, // hence the name arg0. // textbase is an optional output file basename (used only for training) // language is the language code to load. // oem controls which engine(s) will operate on the image // configs (argv) is an array of config filenames to load variables from. // May be NULL. // configs_size (argc) is the number of elements in configs. // vars_vec is an optional vector of variables to set. // vars_values is an optional corresponding vector of values for the variables // in vars_vec. // If set_only_init_params is true, then only the initialization variables // will be set. int Tesseract::init_tesseract_internal( const char *arg0, const char *textbase, const char *language, OcrEngineMode oem, char **configs, int configs_size, const GenericVector *vars_vec, const GenericVector *vars_values, bool set_only_non_debug_params) { if (!init_tesseract_lang_data(arg0, textbase, language, oem, configs, configs_size, vars_vec, vars_values, set_only_non_debug_params)) { return -1; } if (tessedit_init_config_only) { tessdata_manager.End(); return 0; } // If only Cube will be used, skip loading Tesseract classifier's // pre-trained templates. bool init_tesseract_classifier = (tessedit_ocr_engine_mode == OEM_TESSERACT_ONLY || tessedit_ocr_engine_mode == OEM_TESSERACT_CUBE_COMBINED); // If only Cube will be used and if it has its own Unicharset, // skip initializing permuter and loading Tesseract Dawgs. bool init_dict = !(tessedit_ocr_engine_mode == OEM_CUBE_ONLY && tessdata_manager.SeekToStart(TESSDATA_CUBE_UNICHARSET)); program_editup(textbase, init_tesseract_classifier, init_dict); tessdata_manager.End(); return 0; //Normal exit } // Helper builds the all_fonts table by adding new fonts from new_fonts. static void CollectFonts(const UnicityTable& new_fonts, UnicityTable* all_fonts) { for (int i = 0; i < new_fonts.size(); ++i) { // UnicityTable uniques as we go. all_fonts->push_back(new_fonts.get(i)); } } // Helper assigns an id to lang_fonts using the index in all_fonts table. static void AssignIds(const UnicityTable& all_fonts, UnicityTable* lang_fonts) { for (int i = 0; i < lang_fonts->size(); ++i) { int index = all_fonts.get_id(lang_fonts->get(i)); lang_fonts->get_mutable(i)->universal_id = index; } } // Set the universal_id member of each font to be unique among all // instances of the same font loaded. void Tesseract::SetupUniversalFontIds() { // Note that we can get away with bitwise copying FontInfo in // all_fonts, as it is a temporary structure and we avoid setting the // delete callback. UnicityTable all_fonts; all_fonts.set_compare_callback(NewPermanentTessCallback(CompareFontInfo)); // Create the universal ID table. CollectFonts(get_fontinfo_table(), &all_fonts); for (int i = 0; i < sub_langs_.size(); ++i) { CollectFonts(sub_langs_[i]->get_fontinfo_table(), &all_fonts); } // Assign ids from the table to each font table. AssignIds(all_fonts, &get_fontinfo_table()); for (int i = 0; i < sub_langs_.size(); ++i) { AssignIds(all_fonts, &sub_langs_[i]->get_fontinfo_table()); } font_table_size_ = all_fonts.size(); } // init the LM component int Tesseract::init_tesseract_lm(const char *arg0, const char *textbase, const char *language) { if (!init_tesseract_lang_data(arg0, textbase, language, OEM_TESSERACT_ONLY, NULL, 0, NULL, NULL, false)) return -1; getDict().Load(Dict::GlobalDawgCache()); tessdata_manager.End(); return 0; } void Tesseract::end_tesseract() { end_recog(); } /* Define command type identifiers */ enum CMD_EVENTS { ACTION_1_CMD_EVENT, RECOG_WERDS, RECOG_PSEUDO, ACTION_2_CMD_EVENT }; } // namespace tesseract tesseract-3.04.01/ccmain/tessedit.h000066400000000000000000000021571266071204500171130ustar00rootroot00000000000000/********************************************************************** * File: tessedit.h (Formerly tessedit.h) * Description: Main program for merge of tess and editor. * Author: Ray Smith * Created: Tue Jan 07 15:21:46 GMT 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 TESSEDIT_H #define TESSEDIT_H #include "blobs.h" #include "pgedit.h" //progress monitor extern ETEXT_DESC *global_monitor; #endif tesseract-3.04.01/ccmain/tesseract_cube_combiner.cpp000066400000000000000000000272771266071204500225050ustar00rootroot00000000000000/********************************************************************** * File: tesseract_cube_combiner.h * Description: Declaration of the Tesseract & Cube results combiner Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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. * **********************************************************************/ // The TesseractCubeCombiner class provides the functionality of combining // the recognition results of Tesseract and Cube at the word level #include #include #include #include #include "tesseract_cube_combiner.h" #include "cube_object.h" #include "cube_reco_context.h" #include "cube_utils.h" #include "neural_net.h" #include "tesseractclass.h" #include "word_altlist.h" namespace tesseract { TesseractCubeCombiner::TesseractCubeCombiner(CubeRecoContext *cube_cntxt) { cube_cntxt_ = cube_cntxt; combiner_net_ = NULL; } TesseractCubeCombiner::~TesseractCubeCombiner() { if (combiner_net_ != NULL) { delete combiner_net_; combiner_net_ = NULL; } } bool TesseractCubeCombiner::LoadCombinerNet() { ASSERT_HOST(cube_cntxt_); // Compute the path of the combiner net string data_path; cube_cntxt_->GetDataFilePath(&data_path); string net_file_name = data_path + cube_cntxt_->Lang() + ".tesseract_cube.nn"; // Return false if file does not exist FILE *fp = fopen(net_file_name.c_str(), "rb"); if (fp == NULL) return false; else fclose(fp); // Load and validate net combiner_net_ = NeuralNet::FromFile(net_file_name); if (combiner_net_ == NULL) { tprintf("Could not read combiner net file %s", net_file_name.c_str()); return false; } else if (combiner_net_->out_cnt() != 2) { tprintf("Invalid combiner net file %s! Output count != 2\n", net_file_name.c_str()); delete combiner_net_; combiner_net_ = NULL; return false; } return true; } // Normalize a UTF-8 string. Converts the UTF-8 string to UTF32 and optionally // strips punc and/or normalizes case and then converts back string TesseractCubeCombiner::NormalizeString(const string &str, bool remove_punc, bool norm_case) { // convert to UTF32 string_32 str32; CubeUtils::UTF8ToUTF32(str.c_str(), &str32); // strip punc and normalize string_32 new_str32; for (int idx = 0; idx < str32.length(); idx++) { // if no punc removal is required or not a punctuation character if (!remove_punc || iswpunct(str32[idx]) == 0) { char_32 norm_char = str32[idx]; // normalize case if required if (norm_case && iswalpha(norm_char)) { norm_char = towlower(norm_char); } new_str32.push_back(norm_char); } } // convert back to UTF8 string new_str; CubeUtils::UTF32ToUTF8(new_str32.c_str(), &new_str); return new_str; } // Compares 2 strings optionally ignoring punctuation int TesseractCubeCombiner::CompareStrings(const string &str1, const string &str2, bool ignore_punc, bool ignore_case) { if (!ignore_punc && !ignore_case) { return str1.compare(str2); } string norm_str1 = NormalizeString(str1, ignore_punc, ignore_case); string norm_str2 = NormalizeString(str2, ignore_punc, ignore_case); return norm_str1.compare(norm_str2); } // Check if a string is a valid Tess dict word or not bool TesseractCubeCombiner::ValidWord(const string &str) { return (cube_cntxt_->TesseractObject()->getDict().valid_word(str.c_str()) > 0); } // Public method for computing the combiner features. The agreement // output parameter will be true if both answers are identical, // and false otherwise. bool TesseractCubeCombiner::ComputeCombinerFeatures(const string &tess_str, int tess_confidence, CubeObject *cube_obj, WordAltList *cube_alt_list, vector *features, bool *agreement) { features->clear(); *agreement = false; if (cube_alt_list == NULL || cube_alt_list->AltCount() <= 0) return false; // Get Cube's best string; return false if empty char_32 *cube_best_str32 = cube_alt_list->Alt(0); if (cube_best_str32 == NULL || CubeUtils::StrLen(cube_best_str32) < 1) return false; string cube_best_str; int cube_best_cost = cube_alt_list->AltCost(0); int cube_best_bigram_cost = 0; bool cube_best_bigram_cost_valid = true; if (cube_cntxt_->Bigrams()) cube_best_bigram_cost = cube_cntxt_->Bigrams()-> Cost(cube_best_str32, cube_cntxt_->CharacterSet()); else cube_best_bigram_cost_valid = false; CubeUtils::UTF32ToUTF8(cube_best_str32, &cube_best_str); // Get Tesseract's UTF32 string string_32 tess_str32; CubeUtils::UTF8ToUTF32(tess_str.c_str(), &tess_str32); // Compute agreement flag *agreement = (tess_str.compare(cube_best_str) == 0); // Get Cube's second best string; if empty, return false char_32 *cube_next_best_str32; string cube_next_best_str; int cube_next_best_cost = WORST_COST; if (cube_alt_list->AltCount() > 1) { cube_next_best_str32 = cube_alt_list->Alt(1); if (cube_next_best_str32 == NULL || CubeUtils::StrLen(cube_next_best_str32) == 0) { return false; } cube_next_best_cost = cube_alt_list->AltCost(1); CubeUtils::UTF32ToUTF8(cube_next_best_str32, &cube_next_best_str); } // Rank of Tesseract's top result in Cube's alternate list int tess_rank = 0; for (tess_rank = 0; tess_rank < cube_alt_list->AltCount(); tess_rank++) { string alt_str; CubeUtils::UTF32ToUTF8(cube_alt_list->Alt(tess_rank), &alt_str); if (alt_str == tess_str) break; } // Cube's cost for tesseract's result. Note that this modifies the // state of cube_obj, including its alternate list by calling RecognizeWord() int tess_cost = cube_obj->WordCost(tess_str.c_str()); // Cube's bigram cost of Tesseract's string int tess_bigram_cost = 0; int tess_bigram_cost_valid = true; if (cube_cntxt_->Bigrams()) tess_bigram_cost = cube_cntxt_->Bigrams()-> Cost(tess_str32.c_str(), cube_cntxt_->CharacterSet()); else tess_bigram_cost_valid = false; // Tesseract confidence features->push_back(tess_confidence); // Cube cost of Tesseract string features->push_back(tess_cost); // Cube Rank of Tesseract string features->push_back(tess_rank); // length of Tesseract OCR string features->push_back(tess_str.length()); // Tesseract OCR string in dictionary features->push_back(ValidWord(tess_str)); if (tess_bigram_cost_valid) { // bigram cost of Tesseract string features->push_back(tess_bigram_cost); } // Cube tess_cost of Cube best string features->push_back(cube_best_cost); // Cube tess_cost of Cube next best string features->push_back(cube_next_best_cost); // length of Cube string features->push_back(cube_best_str.length()); // Cube string in dictionary features->push_back(ValidWord(cube_best_str)); if (cube_best_bigram_cost_valid) { // bigram cost of Cube string features->push_back(cube_best_bigram_cost); } // case-insensitive string comparison, including punctuation 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, 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, tess_str, true, true); features->push_back(compare_nocase_nopunc == 0); return true; } // The CubeObject parameter is used for 2 purposes: 1) to retrieve // cube's alt list, and 2) to compute cube's word cost for the // tesseract result. The call to CubeObject::WordCost() modifies // the object's alternate list, so previous state will be lost. float TesseractCubeCombiner::CombineResults(WERD_RES *tess_res, CubeObject *cube_obj) { // If no combiner is loaded or the cube object is undefined, // tesseract wins with probability 1.0 if (combiner_net_ == NULL || cube_obj == NULL) { tprintf("Cube WARNING (TesseractCubeCombiner::CombineResults): " "Cube objects not initialized; defaulting to Tesseract\n"); return 1.0; } // Retrieve the alternate list from the CubeObject's current state. // If the alt list empty, tesseract wins with probability 1.0 WordAltList *cube_alt_list = cube_obj->AlternateList(); if (cube_alt_list == NULL) cube_alt_list = cube_obj->RecognizeWord(); if (cube_alt_list == NULL || cube_alt_list->AltCount() <= 0) { tprintf("Cube WARNING (TesseractCubeCombiner::CombineResults): " "Cube returned no results; defaulting to Tesseract\n"); return 1.0; } return CombineResults(tess_res, cube_obj, cube_alt_list); } // The alt_list parameter is expected to have been extracted from the // CubeObject that recognized the word to be combined. The cube_obj // parameter passed may be either same instance or a separate instance to // be used only by the combiner. In both cases, its alternate // list will be modified by an internal call to RecognizeWord(). float TesseractCubeCombiner::CombineResults(WERD_RES *tess_res, CubeObject *cube_obj, WordAltList *cube_alt_list) { // If no combiner is loaded or the cube object is undefined, or the // alt list is empty, tesseract wins with probability 1.0 if (combiner_net_ == NULL || cube_obj == NULL || cube_alt_list == NULL || cube_alt_list->AltCount() <= 0) { tprintf("Cube WARNING (TesseractCubeCombiner::CombineResults): " "Cube result cannot be retrieved; defaulting to Tesseract\n"); return 1.0; } // Tesseract result string, tesseract confidence, and cost of // tesseract result according to cube string tess_str = tess_res->best_choice->unichar_string().string(); // Map certainty [-20.0, 0.0] to confidence [0, 100] int tess_confidence = MIN(100, MAX(1, static_cast( 100 + (5 * tess_res->best_choice->certainty())))); // Compute the combiner features. If feature computation fails or // answers are identical, tesseract wins with probability 1.0 vector features; bool agreement; bool combiner_success = ComputeCombinerFeatures(tess_str, tess_confidence, cube_obj, cube_alt_list, &features, &agreement); if (!combiner_success || agreement) return 1.0; // Classify combiner feature vector and return output (probability // of tesseract class). double net_out[2]; if (!combiner_net_->FeedForward(&features[0], net_out)) return 1.0; return net_out[1]; } } tesseract-3.04.01/ccmain/tesseract_cube_combiner.h000066400000000000000000000075771266071204500221530ustar00rootroot00000000000000/********************************************************************** * File: tesseract_cube_combiner.h * Description: Declaration of the Tesseract & Cube results combiner Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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. * **********************************************************************/ // The TesseractCubeCombiner class provides the functionality of combining // the recognition results of Tesseract and Cube at the word level #ifndef TESSERACT_CCMAIN_TESSERACT_CUBE_COMBINER_H #define TESSERACT_CCMAIN_TESSERACT_CUBE_COMBINER_H #include #include #include "pageres.h" #ifdef _WIN32 #include using namespace std; #endif #ifdef USE_STD_NAMESPACE using std::string; using std::vector; #endif namespace tesseract { class CubeObject; class NeuralNet; class CubeRecoContext; class WordAltList; class TesseractCubeCombiner { public: explicit TesseractCubeCombiner(CubeRecoContext *cube_cntxt); virtual ~TesseractCubeCombiner(); // There are 2 public methods for combining the results of tesseract // and cube. Both return the probability that the Tesseract result is // correct. The difference between the two interfaces is in how the // passed-in CubeObject is used. // The CubeObject parameter is used for 2 purposes: 1) to retrieve // cube's alt list, and 2) to compute cube's word cost for the // tesseract result. Both uses may modify the state of the // CubeObject (including the BeamSearch state) with a call to // RecognizeWord(). float CombineResults(WERD_RES *tess_res, CubeObject *cube_obj); // The alt_list parameter is expected to have been extracted from the // CubeObject that recognized the word to be combined. The cube_obj // parameter passed in is a separate instance to be used only by // the combiner. float CombineResults(WERD_RES *tess_res, CubeObject *cube_obj, WordAltList *alt_list); // Public method for computing the combiner features. The agreement // output parameter will be true if both answers are identical, // false otherwise. Modifies the cube_alt_list, so no assumptions // should be made about its state upon return. bool ComputeCombinerFeatures(const string &tess_res, int tess_confidence, CubeObject *cube_obj, WordAltList *cube_alt_list, vector *features, bool *agreement); // Is the word valid according to Tesseract's language model bool ValidWord(const string &str); // Loads the combiner neural network from file, using cube_cntxt_ // to find path. bool LoadCombinerNet(); private: // Normalize a UTF-8 string. Converts the UTF-8 string to UTF32 and optionally // strips punc and/or normalizes case and then converts back string NormalizeString(const string &str, bool remove_punc, bool norm_case); // Compares 2 strings after optionally normalizing them and or stripping // punctuation int CompareStrings(const string &str1, const string &str2, bool ignore_punc, bool norm_case); NeuralNet *combiner_net_; // pointer to the combiner NeuralNet object CubeRecoContext *cube_cntxt_; // used for language ID and data paths }; } #endif // TESSERACT_CCMAIN_TESSERACT_CUBE_COMBINER_H tesseract-3.04.01/ccmain/tesseractclass.cpp000066400000000000000000001137661266071204500206560ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: tesseractclass.cpp // Description: The Tesseract class. It holds/owns everything needed // to run Tesseract on a single language, and also a set of // sub-Tesseracts to run sub-languages. For thread safety, *every* // variable that was previously global or static (except for // constant data, and some visual debugging flags) has been moved // in here, directly, or indirectly. // This makes it safe to run multiple Tesseracts in different // threads in parallel, and keeps the different language // instances separate. // Some global functions remain, but they are isolated re-entrant // functions that operate on their arguments. Functions that work // on variable data have been moved to an appropriate class based // mostly on the directory hierarchy. For more information see // slide 6 of "2ArchitectureAndDataStructures" in // https://drive.google.com/file/d/0B7l10Bj_LprhbUlIUFlCdGtDYkE/edit?usp=sharing // Some global data and related functions still exist in the // training-related code, but they don't interfere with normal // recognition operation. // Author: Ray Smith // Created: Fri Mar 07 08:17:01 PST 2008 // // (C) Copyright 2008, 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 automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "tesseractclass.h" #include "allheaders.h" #ifndef NO_CUBE_BUILD #include "cube_reco_context.h" #endif #include "edgblob.h" #include "equationdetect.h" #include "globals.h" #ifndef NO_CUBE_BUILD #include "tesseract_cube_combiner.h" #endif namespace tesseract { Tesseract::Tesseract() : BOOL_MEMBER(tessedit_resegment_from_boxes, false, "Take segmentation and labeling from box file", this->params()), BOOL_MEMBER(tessedit_resegment_from_line_boxes, false, "Conversion of word/line box file to char box file", this->params()), BOOL_MEMBER(tessedit_train_from_boxes, false, "Generate training data from boxed chars", this->params()), BOOL_MEMBER(tessedit_make_boxes_from_boxes, false, "Generate more boxes from boxed chars", this->params()), BOOL_MEMBER(tessedit_dump_pageseg_images, false, "Dump intermediate images made during page segmentation", this->params()), // The default for pageseg_mode is the old behaviour, so as not to // upset anything that relies on that. INT_MEMBER( tessedit_pageseg_mode, PSM_SINGLE_BLOCK, "Page seg mode: 0=osd only, 1=auto+osd, 2=auto, 3=col, 4=block," " 5=line, 6=word, 7=char" " (Values from PageSegMode enum in publictypes.h)", this->params()), INT_INIT_MEMBER(tessedit_ocr_engine_mode, tesseract::OEM_TESSERACT_ONLY, "Which OCR engine(s) to run (Tesseract, Cube, both)." " Defaults to loading and running only Tesseract" " (no Cube,no combiner)." " Values from OcrEngineMode enum in tesseractclass.h)", this->params()), STRING_MEMBER(tessedit_char_blacklist, "", "Blacklist of chars not to recognize", this->params()), STRING_MEMBER(tessedit_char_whitelist, "", "Whitelist of chars to recognize", this->params()), STRING_MEMBER(tessedit_char_unblacklist, "", "List of chars to override tessedit_char_blacklist", this->params()), BOOL_MEMBER(tessedit_ambigs_training, false, "Perform training for ambiguities", this->params()), INT_MEMBER(pageseg_devanagari_split_strategy, tesseract::ShiroRekhaSplitter::NO_SPLIT, "Whether to use the top-line splitting process for Devanagari " "documents while performing page-segmentation.", this->params()), INT_MEMBER(ocr_devanagari_split_strategy, tesseract::ShiroRekhaSplitter::NO_SPLIT, "Whether to use the top-line splitting process for Devanagari " "documents while performing ocr.", this->params()), STRING_MEMBER(tessedit_write_params_to_file, "", "Write all parameters to the given file.", this->params()), BOOL_MEMBER(tessedit_adaption_debug, false, "Generate and print debug" " information for adaption", this->params()), INT_MEMBER(bidi_debug, 0, "Debug level for BiDi", this->params()), INT_MEMBER(applybox_debug, 1, "Debug level", this->params()), INT_MEMBER(applybox_page, 0, "Page number to apply boxes from", this->params()), STRING_MEMBER(applybox_exposure_pattern, ".exp", "Exposure value follows" " this pattern in the image filename. The name of the image" " files are expected to be in the form" " [lang].[fontname].exp[num].tif", this->params()), BOOL_MEMBER(applybox_learn_chars_and_char_frags_mode, false, "Learn both character fragments (as is done in the" " special low exposure mode) as well as unfragmented" " characters.", this->params()), BOOL_MEMBER(applybox_learn_ngrams_mode, false, "Each bounding box" " is assumed to contain ngrams. Only learn the ngrams" " whose outlines overlap horizontally.", this->params()), BOOL_MEMBER(tessedit_display_outwords, false, "Draw output words", this->params()), BOOL_MEMBER(tessedit_dump_choices, false, "Dump char choices", this->params()), BOOL_MEMBER(tessedit_timing_debug, false, "Print timing stats", this->params()), BOOL_MEMBER(tessedit_fix_fuzzy_spaces, true, "Try to improve fuzzy spaces", this->params()), BOOL_MEMBER(tessedit_unrej_any_wd, false, "Don't bother with word plausibility", this->params()), BOOL_MEMBER(tessedit_fix_hyphens, true, "Crunch double hyphens?", this->params()), BOOL_MEMBER(tessedit_redo_xheight, true, "Check/Correct x-height", this->params()), BOOL_MEMBER(tessedit_enable_doc_dict, true, "Add words to the document dictionary", this->params()), BOOL_MEMBER(tessedit_debug_fonts, false, "Output font info per char", this->params()), BOOL_MEMBER(tessedit_debug_block_rejection, false, "Block and Row stats", this->params()), BOOL_MEMBER(tessedit_enable_bigram_correction, true, "Enable correction based on the word bigram dictionary.", this->params()), BOOL_MEMBER(tessedit_enable_dict_correction, false, "Enable single word correction based on the dictionary.", this->params()), INT_MEMBER(tessedit_bigram_debug, 0, "Amount of debug output for bigram correction.", this->params()), BOOL_MEMBER(enable_noise_removal, true, "Remove and conditionally reassign small outlines when they" " confuse layout analysis, determining diacritics vs noise", this->params()), INT_MEMBER(debug_noise_removal, 0, "Debug reassignment of small outlines", this->params()), // Worst (min) certainty, for which a diacritic is allowed to make the // base // character worse and still be included. double_MEMBER(noise_cert_basechar, -8.0, "Hingepoint for base char certainty", this->params()), // Worst (min) certainty, for which a non-overlapping diacritic is allowed // to make the base character worse and still be included. double_MEMBER(noise_cert_disjoint, -1.0, "Hingepoint for disjoint certainty", this->params()), // Worst (min) certainty, for which a diacritic is allowed to make a new // stand-alone blob. double_MEMBER(noise_cert_punc, -3.0, "Threshold for new punc char certainty", this->params()), // Factor of certainty margin for adding diacritics to not count as worse. double_MEMBER(noise_cert_factor, 0.375, "Scaling on certainty diff from Hingepoint", this->params()), INT_MEMBER(noise_maxperblob, 8, "Max diacritics to apply to a blob", this->params()), INT_MEMBER(noise_maxperword, 16, "Max diacritics to apply to a word", this->params()), INT_MEMBER(debug_x_ht_level, 0, "Reestimate debug", this->params()), BOOL_MEMBER(debug_acceptable_wds, false, "Dump word pass/fail chk", this->params()), STRING_MEMBER(chs_leading_punct, "('`\"", "Leading punctuation", this->params()), STRING_MEMBER(chs_trailing_punct1, ").,;:?!", "1st Trailing punctuation", this->params()), STRING_MEMBER(chs_trailing_punct2, ")'`\"", "2nd Trailing punctuation", this->params()), double_MEMBER(quality_rej_pc, 0.08, "good_quality_doc lte rejection limit", this->params()), double_MEMBER(quality_blob_pc, 0.0, "good_quality_doc gte good blobs limit", this->params()), double_MEMBER(quality_outline_pc, 1.0, "good_quality_doc lte outline error limit", this->params()), double_MEMBER(quality_char_pc, 0.95, "good_quality_doc gte good char limit", this->params()), INT_MEMBER(quality_min_initial_alphas_reqd, 2, "alphas in a good word", this->params()), INT_MEMBER(tessedit_tess_adaption_mode, 0x27, "Adaptation decision algorithm for tess", this->params()), BOOL_MEMBER(tessedit_minimal_rej_pass1, false, "Do minimal rejection on pass 1 output", this->params()), BOOL_MEMBER(tessedit_test_adaption, false, "Test adaption criteria", this->params()), BOOL_MEMBER(tessedit_matcher_log, false, "Log matcher activity", this->params()), INT_MEMBER(tessedit_test_adaption_mode, 3, "Adaptation decision algorithm for tess", this->params()), BOOL_MEMBER(test_pt, false, "Test for point", this->params()), double_MEMBER(test_pt_x, 99999.99, "xcoord", this->params()), double_MEMBER(test_pt_y, 99999.99, "ycoord", this->params()), INT_MEMBER(paragraph_debug_level, 0, "Print paragraph debug info.", this->params()), BOOL_MEMBER(paragraph_text_based, true, "Run paragraph detection on the post-text-recognition " "(more accurate)", this->params()), INT_MEMBER(cube_debug_level, 0, "Print cube debug info.", this->params()), STRING_MEMBER(outlines_odd, "%| ", "Non standard number of outlines", this->params()), STRING_MEMBER(outlines_2, "ij!?%\":;", "Non standard number of outlines", this->params()), BOOL_MEMBER(docqual_excuse_outline_errs, false, "Allow outline errs in unrejection?", this->params()), BOOL_MEMBER(tessedit_good_quality_unrej, true, "Reduce rejection on good docs", this->params()), BOOL_MEMBER(tessedit_use_reject_spaces, true, "Reject spaces?", this->params()), double_MEMBER(tessedit_reject_doc_percent, 65.00, "%rej allowed before rej whole doc", this->params()), double_MEMBER(tessedit_reject_block_percent, 45.00, "%rej allowed before rej whole block", this->params()), double_MEMBER(tessedit_reject_row_percent, 40.00, "%rej allowed before rej whole row", this->params()), double_MEMBER(tessedit_whole_wd_rej_row_percent, 70.00, "Number of row rejects in whole word rejects" "which prevents whole row rejection", this->params()), BOOL_MEMBER(tessedit_preserve_blk_rej_perfect_wds, true, "Only rej partially rejected words in block rejection", this->params()), BOOL_MEMBER(tessedit_preserve_row_rej_perfect_wds, true, "Only rej partially rejected words in row rejection", this->params()), BOOL_MEMBER(tessedit_dont_blkrej_good_wds, false, "Use word segmentation quality metric", this->params()), BOOL_MEMBER(tessedit_dont_rowrej_good_wds, false, "Use word segmentation quality metric", this->params()), INT_MEMBER(tessedit_preserve_min_wd_len, 2, "Only preserve wds longer than this", this->params()), BOOL_MEMBER(tessedit_row_rej_good_docs, true, "Apply row rejection to good docs", this->params()), double_MEMBER(tessedit_good_doc_still_rowrej_wd, 1.1, "rej good doc wd if more than this fraction rejected", this->params()), BOOL_MEMBER(tessedit_reject_bad_qual_wds, true, "Reject all bad quality wds", this->params()), BOOL_MEMBER(tessedit_debug_doc_rejection, false, "Page stats", this->params()), BOOL_MEMBER(tessedit_debug_quality_metrics, false, "Output data to debug file", this->params()), BOOL_MEMBER(bland_unrej, false, "unrej potential with no chekcs", this->params()), double_MEMBER(quality_rowrej_pc, 1.1, "good_quality_doc gte good char limit", this->params()), BOOL_MEMBER(unlv_tilde_crunching, true, "Mark v.bad words for tilde crunch", this->params()), BOOL_MEMBER(hocr_font_info, false, "Add font info to hocr output", this->params()), BOOL_MEMBER(crunch_early_merge_tess_fails, true, "Before word crunch?", this->params()), BOOL_MEMBER(crunch_early_convert_bad_unlv_chs, false, "Take out ~^ early?", this->params()), double_MEMBER(crunch_terrible_rating, 80.0, "crunch rating lt this", this->params()), BOOL_MEMBER(crunch_terrible_garbage, true, "As it says", this->params()), double_MEMBER(crunch_poor_garbage_cert, -9.0, "crunch garbage cert lt this", this->params()), double_MEMBER(crunch_poor_garbage_rate, 60, "crunch garbage rating lt this", this->params()), double_MEMBER(crunch_pot_poor_rate, 40, "POTENTIAL crunch rating lt this", this->params()), double_MEMBER(crunch_pot_poor_cert, -8.0, "POTENTIAL crunch cert lt this", this->params()), BOOL_MEMBER(crunch_pot_garbage, true, "POTENTIAL crunch garbage", this->params()), double_MEMBER(crunch_del_rating, 60, "POTENTIAL crunch rating lt this", this->params()), double_MEMBER(crunch_del_cert, -10.0, "POTENTIAL crunch cert lt this", this->params()), double_MEMBER(crunch_del_min_ht, 0.7, "Del if word ht lt xht x this", this->params()), double_MEMBER(crunch_del_max_ht, 3.0, "Del if word ht gt xht x this", this->params()), double_MEMBER(crunch_del_min_width, 3.0, "Del if word width lt xht x this", this->params()), double_MEMBER(crunch_del_high_word, 1.5, "Del if word gt xht x this above bl", this->params()), double_MEMBER(crunch_del_low_word, 0.5, "Del if word gt xht x this below bl", this->params()), double_MEMBER(crunch_small_outlines_size, 0.6, "Small if lt xht x this", this->params()), INT_MEMBER(crunch_rating_max, 10, "For adj length in rating per ch", this->params()), INT_MEMBER(crunch_pot_indicators, 1, "How many potential indicators needed", this->params()), BOOL_MEMBER(crunch_leave_ok_strings, true, "Don't touch sensible strings", this->params()), BOOL_MEMBER(crunch_accept_ok, true, "Use acceptability in okstring", this->params()), BOOL_MEMBER(crunch_leave_accept_strings, false, "Don't pot crunch sensible strings", this->params()), BOOL_MEMBER(crunch_include_numerals, false, "Fiddle alpha figures", this->params()), INT_MEMBER(crunch_leave_lc_strings, 4, "Don't crunch words with long lower case strings", this->params()), INT_MEMBER(crunch_leave_uc_strings, 4, "Don't crunch words with long lower case strings", this->params()), INT_MEMBER(crunch_long_repetitions, 3, "Crunch words with long repetitions", this->params()), INT_MEMBER(crunch_debug, 0, "As it says", this->params()), INT_MEMBER(fixsp_non_noise_limit, 1, "How many non-noise blbs either side?", this->params()), double_MEMBER(fixsp_small_outlines_size, 0.28, "Small if lt xht x this", this->params()), BOOL_MEMBER(tessedit_prefer_joined_punct, false, "Reward punctation joins", this->params()), INT_MEMBER(fixsp_done_mode, 1, "What constitues done for spacing", this->params()), INT_MEMBER(debug_fix_space_level, 0, "Contextual fixspace debug", this->params()), STRING_MEMBER(numeric_punctuation, ".,", "Punct. chs expected WITHIN numbers", this->params()), INT_MEMBER(x_ht_acceptance_tolerance, 8, "Max allowed deviation of blob top outside of font data", this->params()), INT_MEMBER(x_ht_min_change, 8, "Min change in xht before actually trying it", this->params()), INT_MEMBER(superscript_debug, 0, "Debug level for sub & superscript fixer", this->params()), double_MEMBER( superscript_worse_certainty, 2.0, "How many times worse " "certainty does a superscript position glyph need to be for " "us to try classifying it as a char with a different " "baseline?", this->params()), double_MEMBER( superscript_bettered_certainty, 0.97, "What reduction in " "badness do we think sufficient to choose a superscript " "over what we'd thought. For example, a value of 0.6 means " "we want to reduce badness of certainty by at least 40%", this->params()), double_MEMBER(superscript_scaledown_ratio, 0.4, "A superscript scaled down more than this is unbelievably " "small. For example, 0.3 means we expect the font size to " "be no smaller than 30% of the text line font size.", this->params()), double_MEMBER(subscript_max_y_top, 0.5, "Maximum top of a character measured as a multiple of " "x-height above the baseline for us to reconsider whether " "it's a subscript.", this->params()), double_MEMBER(superscript_min_y_bottom, 0.3, "Minimum bottom of a character measured as a multiple of " "x-height above the baseline for us to reconsider whether " "it's a superscript.", this->params()), BOOL_MEMBER(tessedit_write_block_separators, false, "Write block separators in output", this->params()), BOOL_MEMBER(tessedit_write_rep_codes, false, "Write repetition char code", this->params()), BOOL_MEMBER(tessedit_write_unlv, false, "Write .unlv output file", this->params()), BOOL_MEMBER(tessedit_create_txt, false, "Write .txt output file", this->params()), BOOL_MEMBER(tessedit_create_hocr, false, "Write .html hOCR output file", this->params()), BOOL_MEMBER(tessedit_create_pdf, false, "Write .pdf output file", this->params()), STRING_MEMBER(unrecognised_char, "|", "Output char for unidentified blobs", this->params()), INT_MEMBER(suspect_level, 99, "Suspect marker level", this->params()), INT_MEMBER(suspect_space_level, 100, "Min suspect level for rejecting spaces", this->params()), INT_MEMBER(suspect_short_words, 2, "Don't suspect dict wds longer than this", this->params()), BOOL_MEMBER(suspect_constrain_1Il, false, "UNLV keep 1Il chars rejected", this->params()), double_MEMBER(suspect_rating_per_ch, 999.9, "Don't touch bad rating limit", this->params()), double_MEMBER(suspect_accept_rating, -999.9, "Accept good rating limit", this->params()), BOOL_MEMBER(tessedit_minimal_rejection, false, "Only reject tess failures", this->params()), BOOL_MEMBER(tessedit_zero_rejection, false, "Don't reject ANYTHING", this->params()), BOOL_MEMBER(tessedit_word_for_word, false, "Make output have exactly one word per WERD", this->params()), BOOL_MEMBER(tessedit_zero_kelvin_rejection, false, "Don't reject ANYTHING AT ALL", this->params()), BOOL_MEMBER(tessedit_consistent_reps, true, "Force all rep chars the same", this->params()), INT_MEMBER(tessedit_reject_mode, 0, "Rejection algorithm", this->params()), BOOL_MEMBER(tessedit_rejection_debug, false, "Adaption debug", this->params()), BOOL_MEMBER(tessedit_flip_0O, true, "Contextual 0O O0 flips", this->params()), double_MEMBER(tessedit_lower_flip_hyphen, 1.5, "Aspect ratio dot/hyphen test", this->params()), double_MEMBER(tessedit_upper_flip_hyphen, 1.8, "Aspect ratio dot/hyphen test", this->params()), BOOL_MEMBER(rej_trust_doc_dawg, false, "Use DOC dawg in 11l conf. detector", this->params()), BOOL_MEMBER(rej_1Il_use_dict_word, false, "Use dictword test", this->params()), BOOL_MEMBER(rej_1Il_trust_permuter_type, true, "Don't double check", this->params()), BOOL_MEMBER(rej_use_tess_accepted, true, "Individual rejection control", this->params()), BOOL_MEMBER(rej_use_tess_blanks, true, "Individual rejection control", this->params()), BOOL_MEMBER(rej_use_good_perm, true, "Individual rejection control", this->params()), BOOL_MEMBER(rej_use_sensible_wd, false, "Extend permuter check", this->params()), BOOL_MEMBER(rej_alphas_in_number_perm, false, "Extend permuter check", this->params()), double_MEMBER(rej_whole_of_mostly_reject_word_fract, 0.85, "if >this fract", this->params()), INT_MEMBER(tessedit_image_border, 2, "Rej blbs near image edge limit", this->params()), STRING_MEMBER(ok_repeated_ch_non_alphanum_wds, "-?*\075", "Allow NN to unrej", this->params()), STRING_MEMBER(conflict_set_I_l_1, "Il1[]", "Il1 conflict set", this->params()), INT_MEMBER(min_sane_x_ht_pixels, 8, "Reject any x-ht lt or eq than this", this->params()), BOOL_MEMBER(tessedit_create_boxfile, false, "Output text with boxes", this->params()), INT_MEMBER(tessedit_page_number, -1, "-1 -> All pages" " , else specifc page to process", this->params()), BOOL_MEMBER(tessedit_write_images, false, "Capture the image from the IPE", this->params()), BOOL_MEMBER(interactive_display_mode, false, "Run interactively?", this->params()), STRING_MEMBER(file_type, ".tif", "Filename extension", this->params()), BOOL_MEMBER(tessedit_override_permuter, true, "According to dict_word", this->params()), INT_MEMBER(tessdata_manager_debug_level, 0, "Debug level for" " TessdataManager functions.", this->params()), STRING_MEMBER(tessedit_load_sublangs, "", "List of languages to load with this one", this->params()), BOOL_MEMBER(tessedit_use_primary_params_model, false, "In multilingual mode use params model of the" " primary language", this->params()), double_MEMBER(min_orientation_margin, 7.0, "Min acceptable orientation margin", this->params()), BOOL_MEMBER(textord_tabfind_show_vlines, false, "Debug line finding", this->params()), BOOL_MEMBER(textord_use_cjk_fp_model, FALSE, "Use CJK fixed pitch model", this->params()), BOOL_MEMBER(poly_allow_detailed_fx, false, "Allow feature extractors to see the original outline", this->params()), BOOL_INIT_MEMBER(tessedit_init_config_only, false, "Only initialize with the config file. Useful if the " "instance is not going to be used for OCR but say only " "for layout analysis.", this->params()), BOOL_MEMBER(textord_equation_detect, false, "Turn on equation detector", this->params()), BOOL_MEMBER(textord_tabfind_vertical_text, true, "Enable vertical detection", this->params()), BOOL_MEMBER(textord_tabfind_force_vertical_text, false, "Force using vertical text page mode", this->params()), double_MEMBER( textord_tabfind_vertical_text_ratio, 0.5, "Fraction of textlines deemed vertical to use vertical page " "mode", this->params()), double_MEMBER( textord_tabfind_aligned_gap_fraction, 0.75, "Fraction of height used as a minimum gap for aligned blobs.", this->params()), INT_MEMBER(tessedit_parallelize, 0, "Run in parallel where possible", this->params()), BOOL_MEMBER(preserve_interword_spaces, false, "Preserve multiple interword spaces", this->params()), BOOL_MEMBER(include_page_breaks, FALSE, "Include page separator string in output text after each " "image/page.", this->params()), STRING_MEMBER(page_separator, "\f", "Page separator (default is form feed control character)", this->params()), // The following parameters were deprecated and removed from their // original // locations. The parameters are temporarily kept here to give Tesseract // users a chance to updated their [lang].traineddata and config files // without introducing failures during Tesseract initialization. // TODO(ocr-team): remove these parameters from the code once we are // reasonably sure that Tesseract users have updated their data files. // // BEGIN DEPRECATED PARAMETERS BOOL_MEMBER(textord_tabfind_vertical_horizontal_mix, true, "find horizontal lines such as headers in vertical page mode", this->params()), INT_MEMBER(tessedit_ok_mode, 5, "Acceptance decision algorithm", this->params()), BOOL_INIT_MEMBER(load_fixed_length_dawgs, true, "Load fixed length dawgs" " (e.g. for non-space delimited languages)", this->params()), INT_MEMBER(segment_debug, 0, "Debug the whole segmentation process", this->params()), BOOL_MEMBER(permute_debug, 0, "Debug char permutation process", this->params()), double_MEMBER(bestrate_pruning_factor, 2.0, "Multiplying factor of" " current best rate to prune other hypotheses", this->params()), BOOL_MEMBER(permute_script_word, 0, "Turn on word script consistency permuter", this->params()), BOOL_MEMBER(segment_segcost_rating, 0, "incorporate segmentation cost in word rating?", this->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.", this->params()), BOOL_MEMBER(permute_fixed_length_dawg, 0, "Turn on fixed-length phrasebook search permuter", this->params()), BOOL_MEMBER(permute_chartype_word, 0, "Turn on character type (property) consistency permuter", this->params()), double_MEMBER(segment_reward_chartype, 0.97, "Score multipler for char type consistency within a word. ", this->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).", this->params()), BOOL_MEMBER(ngram_permuter_activated, false, "Activate character-level n-gram-based permuter", this->params()), BOOL_MEMBER(permute_only_top, false, "Run only the top choice permuter", this->params()), INT_MEMBER(language_model_fixed_length_choices_depth, 3, "Depth of blob choice lists to explore" " when fixed length dawgs are on", this->params()), BOOL_MEMBER(use_new_state_cost, FALSE, "use new state cost heuristics for segmentation state" " evaluation", this->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.", this->params()), double_MEMBER(heuristic_weight_rating, 1.0, "weight associated with char rating in combined cost of" "state", this->params()), double_MEMBER(heuristic_weight_width, 1000.0, "weight associated with width evidence in combined cost of" " state", this->params()), double_MEMBER(heuristic_weight_seamcut, 0.0, "weight associated with seam cut in combined cost of state", this->params()), double_MEMBER(heuristic_max_char_wh_ratio, 2.0, "max char width-to-height ratio allowed in segmentation", this->params()), BOOL_MEMBER(enable_new_segsearch, true, "Enable new segmentation search path.", this->params()), double_MEMBER(segsearch_max_fixed_pitch_char_wh_ratio, 2.0, "Maximum character width-to-height ratio for" " fixed-pitch fonts", this->params()), // END DEPRECATED PARAMETERS backup_config_file_(NULL), pix_binary_(NULL), cube_binary_(NULL), pix_grey_(NULL), pix_thresholds_(NULL), source_resolution_(0), textord_(this), right_to_left_(false), scaled_color_(NULL), scaled_factor_(-1), deskew_(1.0f, 0.0f), reskew_(1.0f, 0.0f), most_recently_used_(this), font_table_size_(0), #ifndef NO_CUBE_BUILD cube_cntxt_(NULL), tess_cube_combiner_(NULL), #endif equ_detect_(NULL) { } Tesseract::~Tesseract() { Clear(); end_tesseract(); sub_langs_.delete_data_pointers(); #ifndef NO_CUBE_BUILD // Delete cube objects. if (cube_cntxt_ != NULL) { delete cube_cntxt_; cube_cntxt_ = NULL; } if (tess_cube_combiner_ != NULL) { delete tess_cube_combiner_; tess_cube_combiner_ = NULL; } #endif } void Tesseract::Clear() { pixDestroy(&pix_binary_); pixDestroy(&cube_binary_); pixDestroy(&pix_grey_); pixDestroy(&pix_thresholds_); pixDestroy(&scaled_color_); deskew_ = FCOORD(1.0f, 0.0f); reskew_ = FCOORD(1.0f, 0.0f); splitter_.Clear(); scaled_factor_ = -1; for (int i = 0; i < sub_langs_.size(); ++i) sub_langs_[i]->Clear(); } void Tesseract::SetEquationDetect(EquationDetect* detector) { equ_detect_ = detector; equ_detect_->SetLangTesseract(this); } // Clear all memory of adaption for this and all subclassifiers. void Tesseract::ResetAdaptiveClassifier() { ResetAdaptiveClassifierInternal(); for (int i = 0; i < sub_langs_.size(); ++i) { sub_langs_[i]->ResetAdaptiveClassifierInternal(); } } // Clear the document dictionary for this and all subclassifiers. void Tesseract::ResetDocumentDictionary() { getDict().ResetDocumentDictionary(); for (int i = 0; i < sub_langs_.size(); ++i) { sub_langs_[i]->getDict().ResetDocumentDictionary(); } } void Tesseract::SetBlackAndWhitelist() { // Set the white and blacklists (if any) unicharset.set_black_and_whitelist(tessedit_char_blacklist.string(), tessedit_char_whitelist.string(), tessedit_char_unblacklist.string()); // Black and white lists should apply to all loaded classifiers. for (int i = 0; i < sub_langs_.size(); ++i) { sub_langs_[i]->unicharset.set_black_and_whitelist( tessedit_char_blacklist.string(), tessedit_char_whitelist.string(), tessedit_char_unblacklist.string()); } } // Perform steps to prepare underlying binary image/other data structures for // page segmentation. void Tesseract::PrepareForPageseg() { textord_.set_use_cjk_fp_model(textord_use_cjk_fp_model); pixDestroy(&cube_binary_); cube_binary_ = pixClone(pix_binary()); // Find the max splitter strategy over all langs. ShiroRekhaSplitter::SplitStrategy max_pageseg_strategy = static_cast( static_cast(pageseg_devanagari_split_strategy)); for (int i = 0; i < sub_langs_.size(); ++i) { ShiroRekhaSplitter::SplitStrategy pageseg_strategy = static_cast( static_cast(sub_langs_[i]->pageseg_devanagari_split_strategy)); if (pageseg_strategy > max_pageseg_strategy) max_pageseg_strategy = pageseg_strategy; // Clone the cube image to all the sub langs too. pixDestroy(&sub_langs_[i]->cube_binary_); sub_langs_[i]->cube_binary_ = pixClone(pix_binary()); pixDestroy(&sub_langs_[i]->pix_binary_); sub_langs_[i]->pix_binary_ = pixClone(pix_binary()); } // Perform shiro-rekha (top-line) splitting and replace the current image by // the newly splitted image. splitter_.set_orig_pix(pix_binary()); splitter_.set_pageseg_split_strategy(max_pageseg_strategy); if (splitter_.Split(true)) { ASSERT_HOST(splitter_.splitted_image()); pixDestroy(&pix_binary_); pix_binary_ = pixClone(splitter_.splitted_image()); } } // Perform steps to prepare underlying binary image/other data structures for // OCR. The current segmentation is required by this method. // Note that this method resets pix_binary_ to the original binarized image, // which may be different from the image actually used for OCR depending on the // value of devanagari_ocr_split_strategy. void Tesseract::PrepareForTessOCR(BLOCK_LIST* block_list, Tesseract* osd_tess, OSResults* osr) { // Find the max splitter strategy over all langs. ShiroRekhaSplitter::SplitStrategy max_ocr_strategy = static_cast( static_cast(ocr_devanagari_split_strategy)); for (int i = 0; i < sub_langs_.size(); ++i) { ShiroRekhaSplitter::SplitStrategy ocr_strategy = static_cast( static_cast(sub_langs_[i]->ocr_devanagari_split_strategy)); if (ocr_strategy > max_ocr_strategy) max_ocr_strategy = ocr_strategy; } // Utilize the segmentation information available. splitter_.set_segmentation_block_list(block_list); splitter_.set_ocr_split_strategy(max_ocr_strategy); // Run the splitter for OCR bool split_for_ocr = splitter_.Split(false); // Restore pix_binary to the binarized original pix for future reference. ASSERT_HOST(splitter_.orig_pix()); pixDestroy(&pix_binary_); pix_binary_ = pixClone(splitter_.orig_pix()); // If the pageseg and ocr strategies are different, refresh the block list // (from the last SegmentImage call) with blobs from the real image to be used // for OCR. if (splitter_.HasDifferentSplitStrategies()) { BLOCK block("", TRUE, 0, 0, 0, 0, pixGetWidth(pix_binary_), pixGetHeight(pix_binary_)); Pix* pix_for_ocr = split_for_ocr ? splitter_.splitted_image() : splitter_.orig_pix(); extract_edges(pix_for_ocr, &block); splitter_.RefreshSegmentationWithNewBlobs(block.blob_list()); } // The splitter isn't needed any more after this, so save memory by clearing. splitter_.Clear(); } } // namespace tesseract tesseract-3.04.01/ccmain/tesseractclass.h000066400000000000000000001711331266071204500203130ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: tesseractclass.h // Description: The Tesseract class. It holds/owns everything needed // to run Tesseract on a single language, and also a set of // sub-Tesseracts to run sub-languages. For thread safety, *every* // global variable goes in here, directly, or indirectly. // This makes it safe to run multiple Tesseracts in different // threads in parallel, and keeps the different language // instances separate. // Author: Ray Smith // Created: Fri Mar 07 08:17:01 PST 2008 // // (C) Copyright 2008, 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_CCMAIN_TESSERACTCLASS_H__ #define TESSERACT_CCMAIN_TESSERACTCLASS_H__ #include "allheaders.h" #include "control.h" #include "docqual.h" #include "devanagari_processing.h" #include "genericvector.h" #include "params.h" #include "ocrclass.h" #include "textord.h" #include "wordrec.h" class BLOB_CHOICE_LIST_CLIST; class BLOCK_LIST; class CharSamp; struct OSResults; class PAGE_RES; class PAGE_RES_IT; struct Pix; class ROW; class SVMenuNode; class TBOX; class TO_BLOCK_LIST; class WERD; class WERD_CHOICE; class WERD_RES; // Top-level class for all tesseract global instance data. // This class either holds or points to all data used by an instance // of Tesseract, including the memory allocator. When this is // complete, Tesseract will be thread-safe. UNTIL THEN, IT IS NOT! // // NOTE to developers: Do not create cyclic dependencies through this class! // The directory dependency tree must remain a tree! The keep this clean, // lower-level code (eg in ccutil, the bottom level) must never need to // know about the content of a higher-level directory. // The following scheme will grant the easiest access to lower-level // global members without creating a cyclic dependency: // // Class Hierarchy (^ = inheritance): // // CCUtil (ccutil/ccutil.h) // ^ Members include: UNICHARSET // CUtil (cutil/cutil_class.h) // ^ Members include: TBLOB*, TEXTBLOCK* // CCStruct (ccstruct/ccstruct.h) // ^ Members include: Image // Classify (classify/classify.h) // ^ Members include: Dict // WordRec (wordrec/wordrec.h) // ^ Members include: WERD*, DENORM* // Tesseract (ccmain/tesseractclass.h) // Members include: Pix*, CubeRecoContext*, // TesseractCubeCombiner* // // Other important classes: // // TessBaseAPI (api/baseapi.h) // Members include: BLOCK_LIST*, PAGE_RES*, // Tesseract*, ImageThresholder* // Dict (dict/dict.h) // Members include: Image* (private) // // NOTE: that each level contains members that correspond to global // data that is defined (and used) at that level, not necessarily where // the type is defined so for instance: // BOOL_VAR_H(textord_show_blobs, false, "Display unsorted blobs"); // goes inside the Textord class, not the cc_util class. namespace tesseract { class ColumnFinder; #ifndef NO_CUBE_BUILD class CubeLineObject; class CubeObject; class CubeRecoContext; #endif class EquationDetect; class Tesseract; #ifndef NO_CUBE_BUILD class TesseractCubeCombiner; #endif // A collection of various variables for statistics and debugging. struct TesseractStats { TesseractStats() : adaption_word_number(0), doc_blob_quality(0), doc_outline_errs(0), doc_char_quality(0), good_char_count(0), doc_good_char_quality(0), word_count(0), dict_words(0), tilde_crunch_written(false), last_char_was_newline(true), last_char_was_tilde(false), write_results_empty_block(true) {} inT32 adaption_word_number; inT16 doc_blob_quality; inT16 doc_outline_errs; inT16 doc_char_quality; inT16 good_char_count; inT16 doc_good_char_quality; inT32 word_count; // count of word in the document inT32 dict_words; // number of dicitionary words in the document STRING dump_words_str; // accumulator used by dump_words() // Flags used by write_results() bool tilde_crunch_written; bool last_char_was_newline; bool last_char_was_tilde; bool write_results_empty_block; }; // Struct to hold all the pointers to relevant data for processing a word. struct WordData { WordData() : word(NULL), row(NULL), block(NULL), prev_word(NULL) {} explicit WordData(const PAGE_RES_IT& page_res_it) : word(page_res_it.word()), row(page_res_it.row()->row), block(page_res_it.block()->block), prev_word(NULL) {} WordData(BLOCK* block_in, ROW* row_in, WERD_RES* word_res) : word(word_res), row(row_in), block(block_in), prev_word(NULL) {} WERD_RES* word; ROW* row; BLOCK* block; WordData* prev_word; PointerVector lang_words; }; // Definition of a Tesseract WordRecognizer. The WordData provides the context // of row/block, in_word holds an initialized, possibly pre-classified word, // that the recognizer may or may not consume (but if so it sets *in_word=NULL) // and produces one or more output words in out_words, which may be the // consumed in_word, or may be generated independently. // This api allows both a conventional tesseract classifier to work, or a // line-level classifier that generates multiple words from a merged input. typedef void (Tesseract::*WordRecognizer)(const WordData& word_data, WERD_RES** in_word, PointerVector* out_words); class Tesseract : public Wordrec { public: Tesseract(); ~Tesseract(); // Clear as much used memory as possible without resetting the adaptive // classifier or losing any other classifier data. void Clear(); // Clear all memory of adaption for this and all subclassifiers. void ResetAdaptiveClassifier(); // Clear the document dictionary for this and all subclassifiers. void ResetDocumentDictionary(); // Set the equation detector. void SetEquationDetect(EquationDetect* detector); // Simple accessors. const FCOORD& reskew() const { return reskew_; } // Destroy any existing pix and return a pointer to the pointer. Pix** mutable_pix_binary() { Clear(); return &pix_binary_; } Pix* pix_binary() const { return pix_binary_; } Pix* pix_grey() const { return pix_grey_; } void set_pix_grey(Pix* grey_pix) { pixDestroy(&pix_grey_); pix_grey_ = grey_pix; } // Returns a pointer to a Pix representing the best available image of the // page. The image will be 8-bit grey if the input was grey or color. Note // that in grey 0 is black and 255 is white. If the input was binary, then // the returned Pix will be binary. Note that here black is 1 and white is 0. // To tell the difference pixGetDepth() will return 8 or 1. // In either case, the return value is a borrowed Pix, and should not be // deleted or pixDestroyed. Pix* BestPix() const { return pix_grey_ != NULL ? pix_grey_ : pix_binary_; } void set_pix_thresholds(Pix* thresholds) { pixDestroy(&pix_thresholds_); pix_thresholds_ = thresholds; } int source_resolution() const { return source_resolution_; } void set_source_resolution(int ppi) { source_resolution_ = ppi; } int ImageWidth() const { return pixGetWidth(pix_binary_); } int ImageHeight() const { return pixGetHeight(pix_binary_); } Pix* scaled_color() const { return scaled_color_; } int scaled_factor() const { return scaled_factor_; } void SetScaledColor(int factor, Pix* color) { scaled_factor_ = factor; scaled_color_ = color; } const Textord& textord() const { return textord_; } Textord* mutable_textord() { return &textord_; } bool right_to_left() const { return right_to_left_; } int num_sub_langs() const { return sub_langs_.size(); } Tesseract* get_sub_lang(int index) const { return sub_langs_[index]; } // Returns true if any language uses Tesseract (as opposed to cube). bool AnyTessLang() const { if (tessedit_ocr_engine_mode != OEM_CUBE_ONLY) return true; for (int i = 0; i < sub_langs_.size(); ++i) { if (sub_langs_[i]->tessedit_ocr_engine_mode != OEM_CUBE_ONLY) return true; } return false; } void SetBlackAndWhitelist(); // Perform steps to prepare underlying binary image/other data structures for // page segmentation. Uses the strategy specified in the global variable // pageseg_devanagari_split_strategy for perform splitting while preparing for // page segmentation. void PrepareForPageseg(); // Perform steps to prepare underlying binary image/other data structures for // Tesseract OCR. The current segmentation is required by this method. // Uses the strategy specified in the global variable // ocr_devanagari_split_strategy for performing splitting while preparing for // Tesseract ocr. void PrepareForTessOCR(BLOCK_LIST* block_list, Tesseract* osd_tess, OSResults* osr); int SegmentPage(const STRING* input_file, BLOCK_LIST* blocks, Tesseract* osd_tess, OSResults* osr); void SetupWordScripts(BLOCK_LIST* blocks); int AutoPageSeg(PageSegMode pageseg_mode, BLOCK_LIST* blocks, TO_BLOCK_LIST* to_blocks, BLOBNBOX_LIST* diacritic_blobs, Tesseract* osd_tess, OSResults* osr); ColumnFinder* SetupPageSegAndDetectOrientation( PageSegMode pageseg_mode, BLOCK_LIST* blocks, Tesseract* osd_tess, OSResults* osr, TO_BLOCK_LIST* to_blocks, Pix** photo_mask_pix, Pix** music_mask_pix); // par_control.cpp void PrerecAllWordsPar(const GenericVector& words); //// control.h ///////////////////////////////////////////////////////// bool ProcessTargetWord(const TBOX& word_box, const TBOX& target_word_box, const char* word_config, int pass); // Sets up the words ready for whichever engine is to be run void SetupAllWordsPassN(int pass_n, const TBOX* target_word_box, const char* word_config, PAGE_RES* page_res, GenericVector* words); // Sets up the single word ready for whichever engine is to be run. void SetupWordPassN(int pass_n, WordData* word); // Runs word recognition on all the words. bool RecogAllWordsPassN(int pass_n, ETEXT_DESC* monitor, PAGE_RES_IT* pr_it, GenericVector* words); bool recog_all_words(PAGE_RES* page_res, ETEXT_DESC* monitor, const TBOX* target_word_box, const char* word_config, int dopasses); void rejection_passes(PAGE_RES* page_res, ETEXT_DESC* monitor, const TBOX* target_word_box, const char* word_config); void bigram_correction_pass(PAGE_RES *page_res); void blamer_pass(PAGE_RES* page_res); // Sets script positions and detects smallcaps on all output words. void script_pos_pass(PAGE_RES* page_res); // Helper to recognize the word using the given (language-specific) tesseract. // Returns positive if this recognizer found more new best words than the // number kept from best_words. int RetryWithLanguage(const WordData& word_data, WordRecognizer recognizer, WERD_RES** in_word, PointerVector* best_words); // Moves good-looking "noise"/diacritics from the reject list to the main // blob list on the current word. Returns true if anything was done, and // sets make_next_word_fuzzy if blob(s) were added to the end of the word. bool ReassignDiacritics(int pass, PAGE_RES_IT* pr_it, bool* make_next_word_fuzzy); // Attempts to put noise/diacritic outlines into the blobs that they overlap. // Input: a set of noisy outlines that probably belong to the real_word. // Output: outlines that overlapped blobs are set to NULL and put back into // the word, either in the blobs or in the reject list. void AssignDiacriticsToOverlappingBlobs( const GenericVector& outlines, int pass, WERD* real_word, PAGE_RES_IT* pr_it, GenericVector* word_wanted, GenericVector* overlapped_any_blob, GenericVector* target_blobs); // Attempts to assign non-overlapping outlines to their nearest blobs or // make new blobs out of them. void AssignDiacriticsToNewBlobs(const GenericVector& outlines, int pass, WERD* real_word, PAGE_RES_IT* pr_it, GenericVector* word_wanted, GenericVector* target_blobs); // Starting with ok_outlines set to indicate which outlines overlap the blob, // chooses the optimal set (approximately) and returns true if any outlines // are desired, in which case ok_outlines indicates which ones. bool SelectGoodDiacriticOutlines(int pass, float certainty_threshold, PAGE_RES_IT* pr_it, C_BLOB* blob, const GenericVector& outlines, int num_outlines, GenericVector* ok_outlines); // Classifies the given blob plus the outlines flagged by ok_outlines, undoes // the inclusion of the outlines, and returns the certainty of the raw choice. float ClassifyBlobPlusOutlines(const GenericVector& ok_outlines, const GenericVector& outlines, int pass_n, PAGE_RES_IT* pr_it, C_BLOB* blob, STRING* best_str); // Classifies the given blob (part of word_data->word->word) as an individual // word, using languages, chopper etc, returning only the certainty of the // best raw choice, and undoing all the work done to fake out the word. float ClassifyBlobAsWord(int pass_n, PAGE_RES_IT* pr_it, C_BLOB* blob, STRING* best_str, float* c2); void classify_word_and_language(int pass_n, PAGE_RES_IT* pr_it, WordData* word_data); void classify_word_pass1(const WordData& word_data, WERD_RES** in_word, PointerVector* out_words); void recog_pseudo_word(PAGE_RES* page_res, // blocks to check TBOX &selection_box); void fix_rep_char(PAGE_RES_IT* page_res_it); ACCEPTABLE_WERD_TYPE acceptable_word_string(const UNICHARSET& char_set, const char *s, const char *lengths); void match_word_pass_n(int pass_n, WERD_RES *word, ROW *row, BLOCK* block); void classify_word_pass2(const WordData& word_data, WERD_RES** in_word, PointerVector* out_words); void ReportXhtFixResult(bool accept_new_word, float new_x_ht, WERD_RES* word, WERD_RES* new_word); bool RunOldFixXht(WERD_RES *word, BLOCK* block, ROW *row); bool TrainedXheightFix(WERD_RES *word, BLOCK* block, ROW *row); // Runs recognition with the test baseline shift and x-height and returns true // if there was an improvement in recognition result. bool TestNewNormalization(int original_misfits, float baseline_shift, float new_x_ht, WERD_RES *word, BLOCK* block, ROW *row); BOOL8 recog_interactive(PAGE_RES_IT* pr_it); // Set fonts of this word. void set_word_fonts(WERD_RES *word); void font_recognition_pass(PAGE_RES* page_res); void dictionary_correction_pass(PAGE_RES* page_res); BOOL8 check_debug_pt(WERD_RES *word, int location); //// superscript.cpp //////////////////////////////////////////////////// bool SubAndSuperscriptFix(WERD_RES *word_res); void GetSubAndSuperscriptCandidates(const WERD_RES *word, int *num_rebuilt_leading, ScriptPos *leading_pos, float *leading_certainty, int *num_rebuilt_trailing, ScriptPos *trailing_pos, float *trailing_certainty, float *avg_certainty, float *unlikely_threshold); WERD_RES *TrySuperscriptSplits(int num_chopped_leading, float leading_certainty, ScriptPos leading_pos, int num_chopped_trailing, float trailing_certainty, ScriptPos trailing_pos, WERD_RES *word, bool *is_good, int *retry_leading, int *retry_trailing); bool BelievableSuperscript(bool debug, const WERD_RES &word, float certainty_threshold, int *left_ok, int *right_ok) const; //// cube_control.cpp /////////////////////////////////////////////////// #ifndef NO_CUBE_BUILD bool init_cube_objects(bool load_combiner, TessdataManager *tessdata_manager); // Iterates through tesseract's results and calls cube on each word, // combining the results with the existing tesseract result. void run_cube_combiner(PAGE_RES *page_res); // Recognizes a single word using (only) cube. Compatible with // Tesseract's classify_word_pass1/classify_word_pass2. void cube_word_pass1(BLOCK* block, ROW *row, WERD_RES *word); // Cube recognizer to recognize a single word as with classify_word_pass1 // but also returns the cube object in case the combiner is needed. CubeObject* cube_recognize_word(BLOCK* block, WERD_RES* word); // Combines the cube and tesseract results for a single word, leaving the // result in tess_word. void cube_combine_word(CubeObject* cube_obj, WERD_RES* cube_word, WERD_RES* tess_word); // Call cube on the current word, and write the result to word. // Sets up a fake result and returns false if something goes wrong. bool cube_recognize(CubeObject *cube_obj, BLOCK* block, WERD_RES *word); void fill_werd_res(const BoxWord& cube_box_word, const char* cube_best_str, WERD_RES* tess_werd_res); bool extract_cube_state(CubeObject* cube_obj, int* num_chars, Boxa** char_boxes, CharSamp*** char_samples); bool create_cube_box_word(Boxa *char_boxes, int num_chars, TBOX word_box, BoxWord* box_word); #endif //// output.h ////////////////////////////////////////////////////////// void output_pass(PAGE_RES_IT &page_res_it, const TBOX *target_word_box); void write_results(PAGE_RES_IT &page_res_it, // full info char newline_type, // type of newline BOOL8 force_eol // override tilde crunch? ); void set_unlv_suspects(WERD_RES *word); UNICHAR_ID get_rep_char(WERD_RES *word); // what char is repeated? BOOL8 acceptable_number_string(const char *s, const char *lengths); inT16 count_alphanums(const WERD_CHOICE &word); inT16 count_alphas(const WERD_CHOICE &word); //// tessedit.h //////////////////////////////////////////////////////// void read_config_file(const char *filename, SetParamConstraint constraint); // Initialize for potentially a set of languages defined by the language // string and recursively any additional languages required by any language // traineddata file (via tessedit_load_sublangs in its config) that is loaded. // See init_tesseract_internal for args. int init_tesseract(const char *arg0, const char *textbase, const char *language, OcrEngineMode oem, char **configs, int configs_size, const GenericVector *vars_vec, const GenericVector *vars_values, bool set_only_init_params); int init_tesseract(const char *datapath, const char *language, OcrEngineMode oem) { return init_tesseract(datapath, NULL, language, oem, NULL, 0, NULL, NULL, false); } // Common initialization for a single language. // arg0 is the datapath for the tessdata directory, which could be the // path of the tessdata directory with no trailing /, or (if tessdata // lives in the same directory as the executable, the path of the executable, // hence the name arg0. // textbase is an optional output file basename (used only for training) // language is the language code to load. // oem controls which engine(s) will operate on the image // configs (argv) is an array of config filenames to load variables from. // May be NULL. // configs_size (argc) is the number of elements in configs. // vars_vec is an optional vector of variables to set. // vars_values is an optional corresponding vector of values for the variables // in vars_vec. // If set_only_init_params is true, then only the initialization variables // will be set. int init_tesseract_internal(const char *arg0, const char *textbase, const char *language, OcrEngineMode oem, char **configs, int configs_size, const GenericVector *vars_vec, const GenericVector *vars_values, bool set_only_init_params); // Set the universal_id member of each font to be unique among all // instances of the same font loaded. void SetupUniversalFontIds(); int init_tesseract_lm(const char *arg0, const char *textbase, const char *language); void recognize_page(STRING& image_name); void end_tesseract(); bool init_tesseract_lang_data(const char *arg0, const char *textbase, const char *language, OcrEngineMode oem, char **configs, int configs_size, const GenericVector *vars_vec, const GenericVector *vars_values, bool set_only_init_params); void ParseLanguageString(const char* lang_str, GenericVector* to_load, GenericVector* not_to_load); //// pgedit.h ////////////////////////////////////////////////////////// SVMenuNode *build_menu_new(); #ifndef GRAPHICS_DISABLED void pgeditor_main(int width, int height, PAGE_RES* page_res); #endif // GRAPHICS_DISABLED void process_image_event( // action in image win const SVEvent &event); BOOL8 process_cmd_win_event( // UI command semantics inT32 cmd_event, // which menu item? char *new_value // any prompt data ); void debug_word(PAGE_RES* page_res, const TBOX &selection_box); void do_re_display( BOOL8 (tesseract::Tesseract::*word_painter)(PAGE_RES_IT* pr_it)); BOOL8 word_display(PAGE_RES_IT* pr_it); BOOL8 word_bln_display(PAGE_RES_IT* pr_it); BOOL8 word_blank_and_set_display(PAGE_RES_IT* pr_its); BOOL8 word_set_display(PAGE_RES_IT* pr_it); // #ifndef GRAPHICS_DISABLED BOOL8 word_dumper(PAGE_RES_IT* pr_it); // #endif // GRAPHICS_DISABLED void blob_feature_display(PAGE_RES* page_res, const TBOX& selection_box); //// reject.h ////////////////////////////////////////////////////////// // make rej map for word void make_reject_map(WERD_RES *word, ROW *row, inT16 pass); BOOL8 one_ell_conflict(WERD_RES *word_res, BOOL8 update_map); inT16 first_alphanum_index(const char *word, const char *word_lengths); inT16 first_alphanum_offset(const char *word, const char *word_lengths); inT16 alpha_count(const char *word, const char *word_lengths); BOOL8 word_contains_non_1_digit(const char *word, const char *word_lengths); void dont_allow_1Il(WERD_RES *word); inT16 count_alphanums( //how many alphanums WERD_RES *word); void flip_0O(WERD_RES *word); BOOL8 non_0_digit(const UNICHARSET& ch_set, UNICHAR_ID unichar_id); BOOL8 non_O_upper(const UNICHARSET& ch_set, UNICHAR_ID unichar_id); BOOL8 repeated_nonalphanum_wd(WERD_RES *word, ROW *row); void nn_match_word( //Match a word WERD_RES *word, ROW *row); void nn_recover_rejects(WERD_RES *word, ROW *row); void set_done( //set done flag WERD_RES *word, inT16 pass); inT16 safe_dict_word(const WERD_RES *werd_res); // is best_choice in dict? void flip_hyphens(WERD_RES *word); void reject_I_1_L(WERD_RES *word); void reject_edge_blobs(WERD_RES *word); void reject_mostly_rejects(WERD_RES *word); //// adaptions.h /////////////////////////////////////////////////////// BOOL8 word_adaptable( //should we adapt? WERD_RES *word, uinT16 mode); //// tfacepp.cpp /////////////////////////////////////////////////////// void recog_word_recursive(WERD_RES* word); void recog_word(WERD_RES *word); void split_and_recog_word(WERD_RES* word); void split_word(WERD_RES *word, int split_pt, WERD_RES **right_piece, BlamerBundle **orig_blamer_bundle) const; void join_words(WERD_RES *word, WERD_RES *word2, BlamerBundle *orig_bb) const; //// fixspace.cpp /////////////////////////////////////////////////////// BOOL8 digit_or_numeric_punct(WERD_RES *word, int char_position); inT16 eval_word_spacing(WERD_RES_LIST &word_res_list); void match_current_words(WERD_RES_LIST &words, ROW *row, BLOCK* block); inT16 fp_eval_word_spacing(WERD_RES_LIST &word_res_list); void fix_noisy_space_list(WERD_RES_LIST &best_perm, ROW *row, BLOCK* block); void fix_fuzzy_space_list(WERD_RES_LIST &best_perm, ROW *row, BLOCK* block); void fix_sp_fp_word(WERD_RES_IT &word_res_it, ROW *row, BLOCK* block); void fix_fuzzy_spaces( //find fuzzy words ETEXT_DESC *monitor, //progress monitor inT32 word_count, //count of words in doc PAGE_RES *page_res); void dump_words(WERD_RES_LIST &perm, inT16 score, inT16 mode, BOOL8 improved); BOOL8 fixspace_thinks_word_done(WERD_RES *word); inT16 worst_noise_blob(WERD_RES *word_res, float *worst_noise_score); float blob_noise_score(TBLOB *blob); void break_noisiest_blob_word(WERD_RES_LIST &words); //// docqual.cpp //////////////////////////////////////////////////////// GARBAGE_LEVEL garbage_word(WERD_RES *word, BOOL8 ok_dict_word); BOOL8 potential_word_crunch(WERD_RES *word, GARBAGE_LEVEL garbage_level, BOOL8 ok_dict_word); void tilde_crunch(PAGE_RES_IT &page_res_it); void unrej_good_quality_words( //unreject potential PAGE_RES_IT &page_res_it); void doc_and_block_rejection( //reject big chunks PAGE_RES_IT &page_res_it, BOOL8 good_quality_doc); void quality_based_rejection(PAGE_RES_IT &page_res_it, BOOL8 good_quality_doc); void convert_bad_unlv_chs(WERD_RES *word_res); void tilde_delete(PAGE_RES_IT &page_res_it); inT16 word_blob_quality(WERD_RES *word, ROW *row); void word_char_quality(WERD_RES *word, ROW *row, inT16 *match_count, inT16 *accepted_match_count); void unrej_good_chs(WERD_RES *word, ROW *row); inT16 count_outline_errs(char c, inT16 outline_count); inT16 word_outline_errs(WERD_RES *word); BOOL8 terrible_word_crunch(WERD_RES *word, GARBAGE_LEVEL garbage_level); CRUNCH_MODE word_deletable(WERD_RES *word, inT16 &delete_mode); inT16 failure_count(WERD_RES *word); BOOL8 noise_outlines(TWERD *word); //// pagewalk.cpp /////////////////////////////////////////////////////// void process_selected_words ( PAGE_RES* page_res, // blocks to check //function to call TBOX & selection_box, BOOL8 (tesseract::Tesseract::*word_processor)(PAGE_RES_IT* pr_it)); //// tessbox.cpp /////////////////////////////////////////////////////// void tess_add_doc_word( //test acceptability WERD_CHOICE *word_choice //after context ); void tess_segment_pass_n(int pass_n, WERD_RES *word); bool tess_acceptable_word(WERD_RES *word); //// applybox.cpp ////////////////////////////////////////////////////// // Applies the box file based on the image name fname, and resegments // the words in the block_list (page), with: // blob-mode: one blob per line in the box file, words as input. // word/line-mode: one blob per space-delimited unit after the #, and one word // per line in the box file. (See comment above for box file format.) // If find_segmentation is true, (word/line mode) then the classifier is used // to re-segment words/lines to match the space-delimited truth string for // each box. In this case, the input box may be for a word or even a whole // text line, and the output words will contain multiple blobs corresponding // to the space-delimited input string. // With find_segmentation false, no classifier is needed, but the chopper // can still be used to correctly segment touching characters with the help // of the input boxes. // In the returned PAGE_RES, the WERD_RES are setup as they would be returned // from normal classification, ie. with a word, chopped_word, rebuild_word, // seam_array, denorm, box_word, and best_state, but NO best_choice or // raw_choice, as they would require a UNICHARSET, which we aim to avoid. // Instead, the correct_text member of WERD_RES is set, and this may be later // converted to a best_choice using CorrectClassifyWords. CorrectClassifyWords // is not required before calling ApplyBoxTraining. PAGE_RES* ApplyBoxes(const STRING& fname, bool find_segmentation, BLOCK_LIST *block_list); // Any row xheight that is significantly different from the median is set // to the median. void PreenXHeights(BLOCK_LIST *block_list); // Builds a PAGE_RES from the block_list in the way required for ApplyBoxes: // All fuzzy spaces are removed, and all the words are maximally chopped. PAGE_RES* SetupApplyBoxes(const GenericVector& boxes, BLOCK_LIST *block_list); // 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. void MaximallyChopWord(const GenericVector& boxes, BLOCK* block, ROW* row, WERD_RES* word_res); // Gather consecutive blobs that match the given box into the best_state // and corresponding correct_text. // Fights over which box owns which blobs are settled by pre-chopping and // applying the blobs to box or next_box with the least non-overlap. // Returns false if the box was in error, which can only be caused by // failing to find an appropriate blob for a box. // This means that occasionally, blobs may be incorrectly segmented if the // chopper fails to find a suitable chop point. bool ResegmentCharBox(PAGE_RES* page_res, const TBOX *prev_box, const TBOX& box, const TBOX& next_box, const char* correct_text); // Consume all source blobs that strongly overlap the given box, // putting them into a new word, with the correct_text label. // Fights over which box owns which blobs are settled by // applying the blobs to box or next_box with the least non-overlap. // Returns false if the box was in error, which can only be caused by // failing to find an overlapping blob for a box. bool ResegmentWordBox(BLOCK_LIST *block_list, const TBOX& box, const TBOX& next_box, const char* correct_text); // Resegments the words by running the classifier in an attempt to find the // correct segmentation that produces the required string. void ReSegmentByClassification(PAGE_RES* page_res); // Converts the space-delimited string of utf8 text to a vector of UNICHAR_ID. // Returns false if an invalid UNICHAR_ID is encountered. bool ConvertStringToUnichars(const char* utf8, GenericVector* class_ids); // Resegments the word to achieve the target_text from the classifier. // Returns false if the re-segmentation fails. // Uses brute-force combination of up to kMaxGroupSize adjacent blobs, and // applies a full search on the classifier results to find the best classified // segmentation. As a compromise to obtain better recall, 1-1 ambigiguity // substitutions ARE used. bool FindSegmentation(const GenericVector& target_text, WERD_RES* word_res); // Recursive helper to find a match to the target_text (from text_index // position) in the choices (from choices_pos position). // Choices is an array of GenericVectors, of length choices_length, with each // element representing a starting position in the word, and the // GenericVector holding classification results for a sequence of consecutive // blobs, with index 0 being a single blob, index 1 being 2 blobs etc. void SearchForText(const GenericVector* choices, int choices_pos, int choices_length, const GenericVector& target_text, int text_index, float rating, GenericVector* segmentation, float* best_rating, GenericVector* best_segmentation); // Counts up the labelled words and the blobs within. // Deletes all unused or emptied words, counting the unused ones. // Resets W_BOL and W_EOL flags correctly. // Builds the rebuild_word and rebuilds the box_word. void TidyUp(PAGE_RES* page_res); // Logs a bad box by line in the box file and box coords. void ReportFailedBox(int boxfile_lineno, TBOX box, const char *box_ch, const char *err_msg); // Creates a fake best_choice entry in each WERD_RES with the correct text. void CorrectClassifyWords(PAGE_RES* page_res); // Call LearnWord to extract features for labelled blobs within each word. // Features are stored in an internal buffer. void ApplyBoxTraining(const STRING& fontname, PAGE_RES* page_res); //// fixxht.cpp /////////////////////////////////////////////////////// // Returns the number of misfit blob tops in this word. int CountMisfitTops(WERD_RES *word_res); // Returns a new x-height in pixels (original image coords) that is // maximally compatible with the result in word_res. // Returns 0.0f if no x-height is found that is better than the current // estimate. float ComputeCompatibleXheight(WERD_RES *word_res, float* baseline_shift); //// Data members /////////////////////////////////////////////////////// // TODO(ocr-team): Find and remove obsolete parameters. BOOL_VAR_H(tessedit_resegment_from_boxes, false, "Take segmentation and labeling from box file"); BOOL_VAR_H(tessedit_resegment_from_line_boxes, false, "Conversion of word/line box file to char box file"); BOOL_VAR_H(tessedit_train_from_boxes, false, "Generate training data from boxed chars"); BOOL_VAR_H(tessedit_make_boxes_from_boxes, false, "Generate more boxes from boxed chars"); BOOL_VAR_H(tessedit_dump_pageseg_images, false, "Dump intermediate images made during page segmentation"); INT_VAR_H(tessedit_pageseg_mode, PSM_SINGLE_BLOCK, "Page seg mode: 0=osd only, 1=auto+osd, 2=auto, 3=col, 4=block," " 5=line, 6=word, 7=char" " (Values from PageSegMode enum in publictypes.h)"); INT_VAR_H(tessedit_ocr_engine_mode, tesseract::OEM_TESSERACT_ONLY, "Which OCR engine(s) to run (Tesseract, Cube, both). Defaults" " to loading and running only Tesseract (no Cube, no combiner)." " (Values from OcrEngineMode enum in tesseractclass.h)"); STRING_VAR_H(tessedit_char_blacklist, "", "Blacklist of chars not to recognize"); STRING_VAR_H(tessedit_char_whitelist, "", "Whitelist of chars to recognize"); STRING_VAR_H(tessedit_char_unblacklist, "", "List of chars to override tessedit_char_blacklist"); BOOL_VAR_H(tessedit_ambigs_training, false, "Perform training for ambiguities"); INT_VAR_H(pageseg_devanagari_split_strategy, tesseract::ShiroRekhaSplitter::NO_SPLIT, "Whether to use the top-line splitting process for Devanagari " "documents while performing page-segmentation."); INT_VAR_H(ocr_devanagari_split_strategy, tesseract::ShiroRekhaSplitter::NO_SPLIT, "Whether to use the top-line splitting process for Devanagari " "documents while performing ocr."); STRING_VAR_H(tessedit_write_params_to_file, "", "Write all parameters to the given file."); BOOL_VAR_H(tessedit_adaption_debug, false, "Generate and print debug information for adaption"); INT_VAR_H(bidi_debug, 0, "Debug level for BiDi"); INT_VAR_H(applybox_debug, 1, "Debug level"); INT_VAR_H(applybox_page, 0, "Page number to apply boxes from"); STRING_VAR_H(applybox_exposure_pattern, ".exp", "Exposure value follows this pattern in the image" " filename. The name of the image files are expected" " to be in the form [lang].[fontname].exp[num].tif"); BOOL_VAR_H(applybox_learn_chars_and_char_frags_mode, false, "Learn both character fragments (as is done in the" " special low exposure mode) as well as unfragmented" " characters."); BOOL_VAR_H(applybox_learn_ngrams_mode, false, "Each bounding box is assumed to contain ngrams. Only" " learn the ngrams whose outlines overlap horizontally."); BOOL_VAR_H(tessedit_display_outwords, false, "Draw output words"); BOOL_VAR_H(tessedit_dump_choices, false, "Dump char choices"); BOOL_VAR_H(tessedit_timing_debug, false, "Print timing stats"); BOOL_VAR_H(tessedit_fix_fuzzy_spaces, true, "Try to improve fuzzy spaces"); BOOL_VAR_H(tessedit_unrej_any_wd, false, "Don't bother with word plausibility"); BOOL_VAR_H(tessedit_fix_hyphens, true, "Crunch double hyphens?"); BOOL_VAR_H(tessedit_redo_xheight, true, "Check/Correct x-height"); BOOL_VAR_H(tessedit_enable_doc_dict, true, "Add words to the document dictionary"); BOOL_VAR_H(tessedit_debug_fonts, false, "Output font info per char"); BOOL_VAR_H(tessedit_debug_block_rejection, false, "Block and Row stats"); BOOL_VAR_H(tessedit_enable_bigram_correction, true, "Enable correction based on the word bigram dictionary."); BOOL_VAR_H(tessedit_enable_dict_correction, false, "Enable single word correction based on the dictionary."); INT_VAR_H(tessedit_bigram_debug, 0, "Amount of debug output for bigram " "correction."); BOOL_VAR_H(enable_noise_removal, true, "Remove and conditionally reassign small outlines when they" " confuse layout analysis, determining diacritics vs noise"); INT_VAR_H(debug_noise_removal, 0, "Debug reassignment of small outlines"); // Worst (min) certainty, for which a diacritic is allowed to make the base // character worse and still be included. double_VAR_H(noise_cert_basechar, -8.0, "Hingepoint for base char certainty"); // Worst (min) certainty, for which a non-overlapping diacritic is allowed to // make the base character worse and still be included. double_VAR_H(noise_cert_disjoint, -2.5, "Hingepoint for disjoint certainty"); // Worst (min) certainty, for which a diacritic is allowed to make a new // stand-alone blob. double_VAR_H(noise_cert_punc, -2.5, "Threshold for new punc char certainty"); // Factor of certainty margin for adding diacritics to not count as worse. double_VAR_H(noise_cert_factor, 0.375, "Scaling on certainty diff from Hingepoint"); INT_VAR_H(noise_maxperblob, 8, "Max diacritics to apply to a blob"); INT_VAR_H(noise_maxperword, 16, "Max diacritics to apply to a word"); INT_VAR_H(debug_x_ht_level, 0, "Reestimate debug"); BOOL_VAR_H(debug_acceptable_wds, false, "Dump word pass/fail chk"); STRING_VAR_H(chs_leading_punct, "('`\"", "Leading punctuation"); STRING_VAR_H(chs_trailing_punct1, ").,;:?!", "1st Trailing punctuation"); STRING_VAR_H(chs_trailing_punct2, ")'`\"", "2nd Trailing punctuation"); double_VAR_H(quality_rej_pc, 0.08, "good_quality_doc lte rejection limit"); double_VAR_H(quality_blob_pc, 0.0, "good_quality_doc gte good blobs limit"); double_VAR_H(quality_outline_pc, 1.0, "good_quality_doc lte outline error limit"); double_VAR_H(quality_char_pc, 0.95, "good_quality_doc gte good char limit"); INT_VAR_H(quality_min_initial_alphas_reqd, 2, "alphas in a good word"); INT_VAR_H(tessedit_tess_adaption_mode, 0x27, "Adaptation decision algorithm for tess"); BOOL_VAR_H(tessedit_minimal_rej_pass1, false, "Do minimal rejection on pass 1 output"); BOOL_VAR_H(tessedit_test_adaption, false, "Test adaption criteria"); BOOL_VAR_H(tessedit_matcher_log, false, "Log matcher activity"); INT_VAR_H(tessedit_test_adaption_mode, 3, "Adaptation decision algorithm for tess"); BOOL_VAR_H(test_pt, false, "Test for point"); double_VAR_H(test_pt_x, 99999.99, "xcoord"); double_VAR_H(test_pt_y, 99999.99, "ycoord"); INT_VAR_H(paragraph_debug_level, 0, "Print paragraph debug info."); BOOL_VAR_H(paragraph_text_based, true, "Run paragraph detection on the post-text-recognition " "(more accurate)"); INT_VAR_H(cube_debug_level, 1, "Print cube debug info."); STRING_VAR_H(outlines_odd, "%| ", "Non standard number of outlines"); STRING_VAR_H(outlines_2, "ij!?%\":;", "Non standard number of outlines"); BOOL_VAR_H(docqual_excuse_outline_errs, false, "Allow outline errs in unrejection?"); BOOL_VAR_H(tessedit_good_quality_unrej, true, "Reduce rejection on good docs"); BOOL_VAR_H(tessedit_use_reject_spaces, true, "Reject spaces?"); double_VAR_H(tessedit_reject_doc_percent, 65.00, "%rej allowed before rej whole doc"); double_VAR_H(tessedit_reject_block_percent, 45.00, "%rej allowed before rej whole block"); double_VAR_H(tessedit_reject_row_percent, 40.00, "%rej allowed before rej whole row"); double_VAR_H(tessedit_whole_wd_rej_row_percent, 70.00, "Number of row rejects in whole word rejects" "which prevents whole row rejection"); BOOL_VAR_H(tessedit_preserve_blk_rej_perfect_wds, true, "Only rej partially rejected words in block rejection"); BOOL_VAR_H(tessedit_preserve_row_rej_perfect_wds, true, "Only rej partially rejected words in row rejection"); BOOL_VAR_H(tessedit_dont_blkrej_good_wds, false, "Use word segmentation quality metric"); BOOL_VAR_H(tessedit_dont_rowrej_good_wds, false, "Use word segmentation quality metric"); INT_VAR_H(tessedit_preserve_min_wd_len, 2, "Only preserve wds longer than this"); BOOL_VAR_H(tessedit_row_rej_good_docs, true, "Apply row rejection to good docs"); double_VAR_H(tessedit_good_doc_still_rowrej_wd, 1.1, "rej good doc wd if more than this fraction rejected"); BOOL_VAR_H(tessedit_reject_bad_qual_wds, true, "Reject all bad quality wds"); BOOL_VAR_H(tessedit_debug_doc_rejection, false, "Page stats"); BOOL_VAR_H(tessedit_debug_quality_metrics, false, "Output data to debug file"); BOOL_VAR_H(bland_unrej, false, "unrej potential with no chekcs"); double_VAR_H(quality_rowrej_pc, 1.1, "good_quality_doc gte good char limit"); BOOL_VAR_H(unlv_tilde_crunching, true, "Mark v.bad words for tilde crunch"); BOOL_VAR_H(hocr_font_info, false, "Add font info to hocr output"); BOOL_VAR_H(crunch_early_merge_tess_fails, true, "Before word crunch?"); BOOL_VAR_H(crunch_early_convert_bad_unlv_chs, false, "Take out ~^ early?"); double_VAR_H(crunch_terrible_rating, 80.0, "crunch rating lt this"); BOOL_VAR_H(crunch_terrible_garbage, true, "As it says"); double_VAR_H(crunch_poor_garbage_cert, -9.0, "crunch garbage cert lt this"); double_VAR_H(crunch_poor_garbage_rate, 60, "crunch garbage rating lt this"); double_VAR_H(crunch_pot_poor_rate, 40, "POTENTIAL crunch rating lt this"); double_VAR_H(crunch_pot_poor_cert, -8.0, "POTENTIAL crunch cert lt this"); BOOL_VAR_H(crunch_pot_garbage, true, "POTENTIAL crunch garbage"); double_VAR_H(crunch_del_rating, 60, "POTENTIAL crunch rating lt this"); double_VAR_H(crunch_del_cert, -10.0, "POTENTIAL crunch cert lt this"); double_VAR_H(crunch_del_min_ht, 0.7, "Del if word ht lt xht x this"); double_VAR_H(crunch_del_max_ht, 3.0, "Del if word ht gt xht x this"); double_VAR_H(crunch_del_min_width, 3.0, "Del if word width lt xht x this"); double_VAR_H(crunch_del_high_word, 1.5, "Del if word gt xht x this above bl"); double_VAR_H(crunch_del_low_word, 0.5, "Del if word gt xht x this below bl"); double_VAR_H(crunch_small_outlines_size, 0.6, "Small if lt xht x this"); INT_VAR_H(crunch_rating_max, 10, "For adj length in rating per ch"); INT_VAR_H(crunch_pot_indicators, 1, "How many potential indicators needed"); BOOL_VAR_H(crunch_leave_ok_strings, true, "Don't touch sensible strings"); BOOL_VAR_H(crunch_accept_ok, true, "Use acceptability in okstring"); BOOL_VAR_H(crunch_leave_accept_strings, false, "Don't pot crunch sensible strings"); BOOL_VAR_H(crunch_include_numerals, false, "Fiddle alpha figures"); INT_VAR_H(crunch_leave_lc_strings, 4, "Don't crunch words with long lower case strings"); INT_VAR_H(crunch_leave_uc_strings, 4, "Don't crunch words with long lower case strings"); INT_VAR_H(crunch_long_repetitions, 3, "Crunch words with long repetitions"); INT_VAR_H(crunch_debug, 0, "As it says"); INT_VAR_H(fixsp_non_noise_limit, 1, "How many non-noise blbs either side?"); double_VAR_H(fixsp_small_outlines_size, 0.28, "Small if lt xht x this"); BOOL_VAR_H(tessedit_prefer_joined_punct, false, "Reward punctation joins"); INT_VAR_H(fixsp_done_mode, 1, "What constitues done for spacing"); INT_VAR_H(debug_fix_space_level, 0, "Contextual fixspace debug"); STRING_VAR_H(numeric_punctuation, ".,", "Punct. chs expected WITHIN numbers"); INT_VAR_H(x_ht_acceptance_tolerance, 8, "Max allowed deviation of blob top outside of font data"); INT_VAR_H(x_ht_min_change, 8, "Min change in xht before actually trying it"); INT_VAR_H(superscript_debug, 0, "Debug level for sub & superscript fixer"); double_VAR_H(superscript_worse_certainty, 2.0, "How many times worse " "certainty does a superscript position glyph need to be for us " "to try classifying it as a char with a different baseline?"); double_VAR_H(superscript_bettered_certainty, 0.97, "What reduction in " "badness do we think sufficient to choose a superscript over " "what we'd thought. For example, a value of 0.6 means we want " "to reduce badness of certainty by 40%"); double_VAR_H(superscript_scaledown_ratio, 0.4, "A superscript scaled down more than this is unbelievably " "small. For example, 0.3 means we expect the font size to " "be no smaller than 30% of the text line font size."); double_VAR_H(subscript_max_y_top, 0.5, "Maximum top of a character measured as a multiple of x-height " "above the baseline for us to reconsider whether it's a " "subscript."); double_VAR_H(superscript_min_y_bottom, 0.3, "Minimum bottom of a character measured as a multiple of " "x-height above the baseline for us to reconsider whether it's " "a superscript."); BOOL_VAR_H(tessedit_write_block_separators, false, "Write block separators in output"); BOOL_VAR_H(tessedit_write_rep_codes, false, "Write repetition char code"); BOOL_VAR_H(tessedit_write_unlv, false, "Write .unlv output file"); BOOL_VAR_H(tessedit_create_txt, false, "Write .txt output file"); BOOL_VAR_H(tessedit_create_hocr, false, "Write .html hOCR output file"); BOOL_VAR_H(tessedit_create_pdf, false, "Write .pdf output file"); STRING_VAR_H(unrecognised_char, "|", "Output char for unidentified blobs"); INT_VAR_H(suspect_level, 99, "Suspect marker level"); INT_VAR_H(suspect_space_level, 100, "Min suspect level for rejecting spaces"); INT_VAR_H(suspect_short_words, 2, "Don't Suspect dict wds longer than this"); BOOL_VAR_H(suspect_constrain_1Il, false, "UNLV keep 1Il chars rejected"); double_VAR_H(suspect_rating_per_ch, 999.9, "Don't touch bad rating limit"); double_VAR_H(suspect_accept_rating, -999.9, "Accept good rating limit"); BOOL_VAR_H(tessedit_minimal_rejection, false, "Only reject tess failures"); BOOL_VAR_H(tessedit_zero_rejection, false, "Don't reject ANYTHING"); BOOL_VAR_H(tessedit_word_for_word, false, "Make output have exactly one word per WERD"); BOOL_VAR_H(tessedit_zero_kelvin_rejection, false, "Don't reject ANYTHING AT ALL"); BOOL_VAR_H(tessedit_consistent_reps, true, "Force all rep chars the same"); INT_VAR_H(tessedit_reject_mode, 0, "Rejection algorithm"); BOOL_VAR_H(tessedit_rejection_debug, false, "Adaption debug"); BOOL_VAR_H(tessedit_flip_0O, true, "Contextual 0O O0 flips"); double_VAR_H(tessedit_lower_flip_hyphen, 1.5, "Aspect ratio dot/hyphen test"); double_VAR_H(tessedit_upper_flip_hyphen, 1.8, "Aspect ratio dot/hyphen test"); BOOL_VAR_H(rej_trust_doc_dawg, false, "Use DOC dawg in 11l conf. detector"); BOOL_VAR_H(rej_1Il_use_dict_word, false, "Use dictword test"); BOOL_VAR_H(rej_1Il_trust_permuter_type, true, "Don't double check"); BOOL_VAR_H(rej_use_tess_accepted, true, "Individual rejection control"); BOOL_VAR_H(rej_use_tess_blanks, true, "Individual rejection control"); BOOL_VAR_H(rej_use_good_perm, true, "Individual rejection control"); BOOL_VAR_H(rej_use_sensible_wd, false, "Extend permuter check"); BOOL_VAR_H(rej_alphas_in_number_perm, false, "Extend permuter check"); double_VAR_H(rej_whole_of_mostly_reject_word_fract, 0.85, "if >this fract"); INT_VAR_H(tessedit_image_border, 2, "Rej blbs near image edge limit"); STRING_VAR_H(ok_repeated_ch_non_alphanum_wds, "-?*\075", "Allow NN to unrej"); STRING_VAR_H(conflict_set_I_l_1, "Il1[]", "Il1 conflict set"); INT_VAR_H(min_sane_x_ht_pixels, 8, "Reject any x-ht lt or eq than this"); BOOL_VAR_H(tessedit_create_boxfile, false, "Output text with boxes"); INT_VAR_H(tessedit_page_number, -1, "-1 -> All pages, else specifc page to process"); BOOL_VAR_H(tessedit_write_images, false, "Capture the image from the IPE"); BOOL_VAR_H(interactive_display_mode, false, "Run interactively?"); STRING_VAR_H(file_type, ".tif", "Filename extension"); BOOL_VAR_H(tessedit_override_permuter, true, "According to dict_word"); INT_VAR_H(tessdata_manager_debug_level, 0, "Debug level for TessdataManager functions."); STRING_VAR_H(tessedit_load_sublangs, "", "List of languages to load with this one"); BOOL_VAR_H(tessedit_use_primary_params_model, false, "In multilingual mode use params model of the primary language"); // Min acceptable orientation margin (difference in scores between top and 2nd // choice in OSResults::orientations) to believe the page orientation. double_VAR_H(min_orientation_margin, 7.0, "Min acceptable orientation margin"); BOOL_VAR_H(textord_tabfind_show_vlines, false, "Debug line finding"); BOOL_VAR_H(textord_use_cjk_fp_model, FALSE, "Use CJK fixed pitch model"); BOOL_VAR_H(poly_allow_detailed_fx, false, "Allow feature extractors to see the original outline"); BOOL_VAR_H(tessedit_init_config_only, false, "Only initialize with the config file. Useful if the instance is " "not going to be used for OCR but say only for layout analysis."); BOOL_VAR_H(textord_equation_detect, false, "Turn on equation detector"); BOOL_VAR_H(textord_tabfind_vertical_text, true, "Enable vertical detection"); BOOL_VAR_H(textord_tabfind_force_vertical_text, false, "Force using vertical text page mode"); double_VAR_H(textord_tabfind_vertical_text_ratio, 0.5, "Fraction of textlines deemed vertical to use vertical page " "mode"); double_VAR_H(textord_tabfind_aligned_gap_fraction, 0.75, "Fraction of height used as a minimum gap for aligned blobs."); INT_VAR_H(tessedit_parallelize, 0, "Run in parallel where possible"); BOOL_VAR_H(preserve_interword_spaces, false, "Preserve multiple interword spaces"); BOOL_VAR_H(include_page_breaks, false, "Include page separator string in output text after each " "image/page."); STRING_VAR_H(page_separator, "\f", "Page separator (default is form feed control character)"); // The following parameters were deprecated and removed from their original // locations. The parameters are temporarily kept here to give Tesseract // users a chance to updated their [lang].traineddata and config files // without introducing failures during Tesseract initialization. // TODO(ocr-team): remove these parameters from the code once we are // reasonably sure that Tesseract users have updated their data files. // // BEGIN DEPRECATED PARAMETERS BOOL_VAR_H(textord_tabfind_vertical_horizontal_mix, true, "find horizontal lines such as headers in vertical page mode"); INT_VAR_H(tessedit_ok_mode, 5, "Acceptance decision algorithm"); BOOL_VAR_H(load_fixed_length_dawgs, true, "Load fixed length" " dawgs (e.g. for non-space delimited languages)"); INT_VAR_H(segment_debug, 0, "Debug the whole segmentation process"); BOOL_VAR_H(permute_debug, 0, "char permutation debug"); 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?"); 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. "); 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(ngram_permuter_activated, false, "Activate character-level n-gram-based permuter"); BOOL_VAR_H(permute_only_top, false, "Run only the top choice permuter"); INT_VAR_H(language_model_fixed_length_choices_depth, 3, "Depth of blob choice lists to explore" " when fixed length dawgs are on"); 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, 1000.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"); BOOL_VAR_H(enable_new_segsearch, false, "Enable new segmentation search path."); double_VAR_H(segsearch_max_fixed_pitch_char_wh_ratio, 2.0, "Maximum character width-to-height ratio for" "fixed pitch fonts"); // END DEPRECATED PARAMETERS //// ambigsrecog.cpp ///////////////////////////////////////////////////////// FILE *init_recog_training(const STRING &fname); void recog_training_segmented(const STRING &fname, PAGE_RES *page_res, volatile ETEXT_DESC *monitor, FILE *output_file); void ambigs_classify_and_output(const char *label, PAGE_RES_IT* pr_it, FILE *output_file); #ifndef NO_CUBE_BUILD inline CubeRecoContext *GetCubeRecoContext() { return cube_cntxt_; } #endif private: // The filename of a backup config file. If not null, then we currently // have a temporary debug config file loaded, and backup_config_file_ // will be loaded, and set to null when debug is complete. const char* backup_config_file_; // The filename of a config file to read when processing a debug word. STRING word_config_; // Image used for input to layout analysis and tesseract recognition. // May be modified by the ShiroRekhaSplitter to eliminate the top-line. Pix* pix_binary_; // Unmodified image used for input to cube. Always valid. Pix* cube_binary_; // Grey-level input image if the input was not binary, otherwise NULL. Pix* pix_grey_; // Thresholds that were used to generate the thresholded image from grey. Pix* pix_thresholds_; // Input image resolution after any scaling. The resolution is not well // transmitted by operations on Pix, so we keep an independent record here. int source_resolution_; // The shiro-rekha splitter object which is used to split top-lines in // Devanagari words to provide a better word and grapheme segmentation. ShiroRekhaSplitter splitter_; // Page segmentation/layout Textord textord_; // True if the primary language uses right_to_left reading order. bool right_to_left_; Pix* scaled_color_; int scaled_factor_; FCOORD deskew_; FCOORD reskew_; TesseractStats stats_; // Sub-languages to be tried in addition to this. GenericVector sub_langs_; // Most recently used Tesseract out of this and sub_langs_. The default // language for the next word. Tesseract* most_recently_used_; // The size of the font table, ie max possible font id + 1. int font_table_size_; #ifndef NO_CUBE_BUILD // Cube objects. CubeRecoContext* cube_cntxt_; TesseractCubeCombiner *tess_cube_combiner_; #endif // Equation detector. Note: this pointer is NOT owned by the class. EquationDetect* equ_detect_; }; } // namespace tesseract #endif // TESSERACT_CCMAIN_TESSERACTCLASS_H__ tesseract-3.04.01/ccmain/tessvars.cpp000066400000000000000000000017741266071204500175000ustar00rootroot00000000000000/********************************************************************** * File: tessvars.cpp (Formerly tessvars.c) * Description: Variables and other globals for tessedit. * Author: Ray Smith * Created: Mon Apr 13 13:13: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. * **********************************************************************/ #include #include "tessvars.h" FILE *debug_fp = stderr; // write debug stuff here tesseract-3.04.01/ccmain/tessvars.h000066400000000000000000000020451266071204500171350ustar00rootroot00000000000000/********************************************************************** * File: tessvars.h (Formerly tessvars.h) * Description: Variables and other globals for tessedit. * Author: Ray Smith * Created: Mon Apr 13 13:13: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 TESSVARS_H #define TESSVARS_H #include extern FILE *debug_fp; // write debug stuff here #endif tesseract-3.04.01/ccmain/tfacepp.cpp000066400000000000000000000314231266071204500172420ustar00rootroot00000000000000/********************************************************************** * File: tfacepp.cpp (Formerly tface++.c) * 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. * **********************************************************************/ #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #pragma warning(disable:4305) // int/float warnings #pragma warning(disable:4800) // int/bool warnings #endif #include #include "blamer.h" #include "errcode.h" #include "ratngs.h" #include "reject.h" #include "tesseractclass.h" #include "werd.h" #define MAX_UNDIVIDED_LENGTH 24 /********************************************************************** * recog_word * * Convert the word to tess form and pass it to the tess segmenter. * Convert the output back to editor form. **********************************************************************/ namespace tesseract { 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()) { tprintf("recog_word ASSERT FAIL String:\"%s\"; " "Strlen=%d; #Blobs=%d\n", word->best_choice->debug_string().string(), word->best_choice->length(), word->box_word->length()); } ASSERT_HOST(word->best_choice->length() == word->box_word->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(); if ((perm_type != SYSTEM_DAWG_PERM) && (perm_type != FREQ_DAWG_PERM) && (perm_type != USER_DAWG_PERM)) { uinT8 real_dict_perm_type = dict_word(*word->best_choice); if (((real_dict_perm_type == SYSTEM_DAWG_PERM) || (real_dict_perm_type == FREQ_DAWG_PERM) || (real_dict_perm_type == USER_DAWG_PERM)) && (alpha_count(word->best_choice->unichar_string().string(), word->best_choice->unichar_lengths().string()) > 0)) { word->best_choice->set_permuter(real_dict_perm_type); // use dict perm } } if (tessedit_rejection_debug && perm_type != word->best_choice->permuter()) { tprintf("Permuter Type Flipped from %d to %d\n", perm_type, word->best_choice->permuter()); } } // Factored out from control.cpp ASSERT_HOST((word->best_choice == NULL) == (word->raw_choice == NULL)); if (word->best_choice == NULL || word->best_choice->length() == 0 || static_cast(strspn(word->best_choice->unichar_string().string(), " ")) == word->best_choice->length()) { word->tess_failed = true; word->reject_map.initialise(word->box_word->length()); word->reject_map.rej_word_tess_failure(); } else { word->tess_failed = false; } } /********************************************************************** * recog_word_recursive * * 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) { int word_length = word->chopped_word->NumBlobs(); // no of blobs if (word_length > MAX_UNDIVIDED_LENGTH) { return split_and_recog_word(word); } cc_recog(word); word_length = word->rebuild_word->NumBlobs(); // No of blobs in output. // Do sanity checks and minor fixes on best_choice. if (word->best_choice->length() > word_length) { word->best_choice->make_bad(); // should never happen tprintf("recog_word: Discarded long string \"%s\"" " (%d characters vs %d blobs)\n", word->best_choice->unichar_string().string(), word->best_choice->length(), word_length); tprintf("Word is at:"); word->word->bounding_box().print(); } if (word->best_choice->length() < word_length) { UNICHAR_ID space_id = unicharset.unichar_to_id(" "); while (word->best_choice->length() < word_length) { word->best_choice->append_unichar_id(space_id, 1, 0.0, word->best_choice->certainty()); } } } /********************************************************************** * split_and_recog_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) { // Find the biggest blob gap in the chopped_word. int bestgap = -MAX_INT32; int split_index = 0; 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; } } 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; 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 = 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) { // 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(); 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); } *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. 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.push_back(new SEAM(0.0f, split_pt)); 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->raw_choice += *word2->raw_choice; // How many alt choices from each should we try to get? const int kAltsPerPiece = 2; // When do we start throwing away extra alt choices? const int kTooManyAltChoices = 100; // 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) { orig_bb->JoinBlames(*word->blamer_bundle, *word2->blamer_bundle, wordrec_debug_blamer); delete word->blamer_bundle; word->blamer_bundle = orig_bb; } word->SetupBoxWord(); word->reject_map.initialise(word->box_word->length()); delete word2; } } // namespace tesseract tesseract-3.04.01/ccmain/thresholder.cpp000066400000000000000000000260061266071204500201440ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: thresholder.cpp // Description: Base API for thresolding images in tesseract. // Author: Ray Smith // Created: Mon May 12 11:28:15 PDT 2008 // // (C) Copyright 2008, 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 "allheaders.h" #include "thresholder.h" #include #include "otsuthr.h" #include "openclwrapper.h" namespace tesseract { ImageThresholder::ImageThresholder() : pix_(NULL), image_width_(0), image_height_(0), pix_channels_(0), pix_wpl_(0), scale_(1), yres_(300), estimated_res_(300) { SetRectangle(0, 0, 0, 0); } ImageThresholder::~ImageThresholder() { Clear(); } // Destroy the Pix if there is one, freeing memory. void ImageThresholder::Clear() { pixDestroy(&pix_); } // Return true if no image has been set. bool ImageThresholder::IsEmpty() const { return pix_ == NULL; } // SetImage makes a copy of all the image data, so it may be deleted // immediately after this call. // Greyscale of 8 and color of 24 or 32 bits per pixel may be given. // Palette color images will not work properly and must be converted to // 24 bit. // Binary images of 1 bit per pixel may also be given but they must be // byte packed with the MSB of the first byte being the first pixel, and a // one pixel is WHITE. For binary images set bytes_per_pixel=0. void ImageThresholder::SetImage(const unsigned char* imagedata, int width, int height, int bytes_per_pixel, int bytes_per_line) { int bpp = bytes_per_pixel * 8; if (bpp == 0) bpp = 1; Pix* pix = pixCreate(width, height, bpp == 24 ? 32 : bpp); l_uint32* data = pixGetData(pix); int wpl = pixGetWpl(pix); switch (bpp) { case 1: for (int y = 0; y < height; ++y, data += wpl, imagedata += bytes_per_line) { for (int x = 0; x < width; ++x) { if (imagedata[x / 8] & (0x80 >> (x % 8))) CLEAR_DATA_BIT(data, x); else SET_DATA_BIT(data, x); } } break; case 8: // Greyscale just copies the bytes in the right order. for (int y = 0; y < height; ++y, data += wpl, imagedata += bytes_per_line) { for (int x = 0; x < width; ++x) SET_DATA_BYTE(data, x, imagedata[x]); } break; case 24: // Put the colors in the correct places in the line buffer. for (int y = 0; y < height; ++y, imagedata += bytes_per_line) { for (int x = 0; x < width; ++x, ++data) { SET_DATA_BYTE(data, COLOR_RED, imagedata[3 * x]); SET_DATA_BYTE(data, COLOR_GREEN, imagedata[3 * x + 1]); SET_DATA_BYTE(data, COLOR_BLUE, imagedata[3 * x + 2]); } } break; case 32: // Maintain byte order consistency across different endianness. for (int y = 0; y < height; ++y, imagedata += bytes_per_line, data += wpl) { for (int x = 0; x < width; ++x) { data[x] = (imagedata[x * 4] << 24) | (imagedata[x * 4 + 1] << 16) | (imagedata[x * 4 + 2] << 8) | imagedata[x * 4 + 3]; } } break; default: tprintf("Cannot convert RAW image to Pix with bpp = %d\n", bpp); } pixSetYRes(pix, 300); SetImage(pix); pixDestroy(&pix); } // Store the coordinates of the rectangle to process for later use. // Doesn't actually do any thresholding. void ImageThresholder::SetRectangle(int left, int top, int width, int height) { rect_left_ = left; rect_top_ = top; rect_width_ = width; rect_height_ = height; } // Get enough parameters to be able to rebuild bounding boxes in the // original image (not just within the rectangle). // Left and top are enough with top-down coordinates, but // the height of the rectangle and the image are needed for bottom-up. void ImageThresholder::GetImageSizes(int* left, int* top, int* width, int* height, int* imagewidth, int* imageheight) { *left = rect_left_; *top = rect_top_; *width = rect_width_; *height = rect_height_; *imagewidth = image_width_; *imageheight = image_height_; } // Pix vs raw, which to use? Pix is the preferred input for efficiency, // since raw buffers are copied. // SetImage for Pix clones its input, so the source pix may be pixDestroyed // immediately after, but may not go away until after the Thresholder has // finished with it. void ImageThresholder::SetImage(const Pix* pix) { if (pix_ != NULL) pixDestroy(&pix_); Pix* src = const_cast(pix); int depth; pixGetDimensions(src, &image_width_, &image_height_, &depth); // Convert the image as necessary so it is one of binary, plain RGB, or // 8 bit with no colormap. if (depth > 1 && depth < 8) { pix_ = pixConvertTo8(src, false); } else if (pixGetColormap(src)) { pix_ = pixRemoveColormap(src, REMOVE_CMAP_BASED_ON_SRC); } else { pix_ = pixClone(src); } depth = pixGetDepth(pix_); pix_channels_ = depth / 8; pix_wpl_ = pixGetWpl(pix_); scale_ = 1; estimated_res_ = yres_ = pixGetYRes(src); Init(); } // Threshold the source image as efficiently as possible to the output Pix. // Creates a Pix and sets pix to point to the resulting pointer. // Caller must use pixDestroy to free the created Pix. void ImageThresholder::ThresholdToPix(PageSegMode pageseg_mode, Pix** pix) { if (pix_channels_ == 0) { // We have a binary image, so it just has to be cloned. *pix = GetPixRect(); } else { OtsuThresholdRectToPix(pix_, pix); } } // Gets a pix that contains an 8 bit threshold value at each pixel. The // returned pix may be an integer reduction of the binary image such that // the scale factor may be inferred from the ratio of the sizes, even down // to the extreme of a 1x1 pixel thresholds image. // Ideally the 8 bit threshold should be the exact threshold used to generate // the binary image in ThresholdToPix, but this is not a hard constraint. // Returns NULL if the input is binary. PixDestroy after use. Pix* ImageThresholder::GetPixRectThresholds() { if (IsBinary()) return NULL; Pix* pix_grey = GetPixRectGrey(); int width = pixGetWidth(pix_grey); int height = pixGetHeight(pix_grey); int* thresholds; int* hi_values; OtsuThreshold(pix_grey, 0, 0, width, height, &thresholds, &hi_values); pixDestroy(&pix_grey); Pix* pix_thresholds = pixCreate(width, height, 8); int threshold = thresholds[0] > 0 ? thresholds[0] : 128; pixSetAllArbitrary(pix_thresholds, threshold); delete [] thresholds; delete [] hi_values; return pix_thresholds; } // Common initialization shared between SetImage methods. void ImageThresholder::Init() { SetRectangle(0, 0, image_width_, image_height_); } // Get a clone/copy of the source image rectangle. // The returned Pix must be pixDestroyed. // This function will be used in the future by the page layout analysis, and // the layout analysis that uses it will only be available with Leptonica, // so there is no raw equivalent. Pix* ImageThresholder::GetPixRect() { if (IsFullImage()) { // Just clone the whole thing. return pixClone(pix_); } else { // Crop to the given rectangle. Box* box = boxCreate(rect_left_, rect_top_, rect_width_, rect_height_); Pix* cropped = pixClipRectangle(pix_, box, NULL); boxDestroy(&box); return cropped; } } // Get a clone/copy of the source image rectangle, reduced to greyscale, // and at the same resolution as the output binary. // The returned Pix must be pixDestroyed. // Provided to the classifier to extract features from the greyscale image. Pix* ImageThresholder::GetPixRectGrey() { Pix* pix = GetPixRect(); // May have to be reduced to grey. int depth = pixGetDepth(pix); if (depth != 8) { Pix* result = depth < 8 ? pixConvertTo8(pix, false) : pixConvertRGBToLuminance(pix); pixDestroy(&pix); return result; } return pix; } // Otsu thresholds the rectangle, taking the rectangle from *this. void ImageThresholder::OtsuThresholdRectToPix(Pix* src_pix, Pix** out_pix) const { PERF_COUNT_START("OtsuThresholdRectToPix") int* thresholds; int* hi_values; int num_channels = OtsuThreshold(src_pix, rect_left_, rect_top_, rect_width_, rect_height_, &thresholds, &hi_values); // only use opencl if compiled w/ OpenCL and selected device is opencl #ifdef USE_OPENCL OpenclDevice od; if ((num_channels == 4 || num_channels == 1) && od.selectedDeviceIsOpenCL() && rect_top_ == 0 && rect_left_ == 0 ) { od.ThresholdRectToPixOCL((const unsigned char*)pixGetData(src_pix), num_channels, pixGetWpl(src_pix) * 4, thresholds, hi_values, out_pix /*pix_OCL*/, rect_height_, rect_width_, rect_top_, rect_left_); } else { #endif ThresholdRectToPix(src_pix, num_channels, thresholds, hi_values, out_pix); #ifdef USE_OPENCL } #endif delete [] thresholds; delete [] hi_values; PERF_COUNT_END } /// Threshold the rectangle, taking everything except the src_pix /// from the class, using thresholds/hi_values to the output pix. /// NOTE that num_channels is the size of the thresholds and hi_values // arrays and also the bytes per pixel in src_pix. void ImageThresholder::ThresholdRectToPix(Pix* src_pix, int num_channels, const int* thresholds, const int* hi_values, Pix** pix) const { PERF_COUNT_START("ThresholdRectToPix") *pix = pixCreate(rect_width_, rect_height_, 1); uinT32* pixdata = pixGetData(*pix); int wpl = pixGetWpl(*pix); int src_wpl = pixGetWpl(src_pix); uinT32* srcdata = pixGetData(src_pix); for (int y = 0; y < rect_height_; ++y) { const uinT32* linedata = srcdata + (y + rect_top_) * src_wpl; uinT32* pixline = pixdata + y * wpl; for (int x = 0; x < rect_width_; ++x) { bool white_result = true; for (int ch = 0; ch < num_channels; ++ch) { int pixel = GET_DATA_BYTE(const_cast( reinterpret_cast(linedata)), (x + rect_left_) * num_channels + ch); if (hi_values[ch] >= 0 && (pixel > thresholds[ch]) == (hi_values[ch] == 0)) { white_result = false; break; } } if (white_result) CLEAR_DATA_BIT(pixline, x); else SET_DATA_BIT(pixline, x); } } PERF_COUNT_END } } // namespace tesseract. tesseract-3.04.01/ccmain/thresholder.h000066400000000000000000000172471266071204500176200ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: thresholder.h // Description: Base API for thresolding images in tesseract. // Author: Ray Smith // Created: Mon May 12 11:00:15 PDT 2008 // // (C) Copyright 2008, 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_CCMAIN_THRESHOLDER_H__ #define TESSERACT_CCMAIN_THRESHOLDER_H__ #include "platform.h" #include "publictypes.h" struct Pix; namespace tesseract { /// Base class for all tesseract image thresholding classes. /// Specific classes can add new thresholding methods by /// overriding ThresholdToPix. /// Each instance deals with a single image, but the design is intended to /// be useful for multiple calls to SetRectangle and ThresholdTo* if /// desired. class TESS_API ImageThresholder { public: ImageThresholder(); virtual ~ImageThresholder(); /// Destroy the Pix if there is one, freeing memory. virtual void Clear(); /// Return true if no image has been set. bool IsEmpty() const; /// SetImage makes a copy of all the image data, so it may be deleted /// immediately after this call. /// Greyscale of 8 and color of 24 or 32 bits per pixel may be given. /// Palette color images will not work properly and must be converted to /// 24 bit. /// Binary images of 1 bit per pixel may also be given but they must be /// byte packed with the MSB of the first byte being the first pixel, and a /// one pixel is WHITE. For binary images set bytes_per_pixel=0. void SetImage(const unsigned char* imagedata, int width, int height, int bytes_per_pixel, int bytes_per_line); /// Store the coordinates of the rectangle to process for later use. /// Doesn't actually do any thresholding. void SetRectangle(int left, int top, int width, int height); /// Get enough parameters to be able to rebuild bounding boxes in the /// original image (not just within the rectangle). /// Left and top are enough with top-down coordinates, but /// the height of the rectangle and the image are needed for bottom-up. virtual void GetImageSizes(int* left, int* top, int* width, int* height, int* imagewidth, int* imageheight); /// Return true if the source image is color. bool IsColor() const { return pix_channels_ >= 3; } /// Returns true if the source image is binary. bool IsBinary() const { return pix_channels_ == 0; } int GetScaleFactor() const { return scale_; } // Set the resolution of the source image in pixels per inch. // This should be called right after SetImage(), and will let us return // appropriate font sizes for the text. void SetSourceYResolution(int ppi) { yres_ = ppi; estimated_res_ = ppi; } int GetSourceYResolution() const { return yres_; } int GetScaledYResolution() const { return scale_ * yres_; } // Set the resolution of the source image in pixels per inch, as estimated // by the thresholder from the text size found during thresholding. // This value will be used to set internal size thresholds during recognition // and will not influence the output "point size." The default value is // the same as the source resolution. (yres_) void SetEstimatedResolution(int ppi) { estimated_res_ = ppi; } // Returns the estimated resolution, including any active scaling. // This value will be used to set internal size thresholds during recognition. int GetScaledEstimatedResolution() const { return scale_ * estimated_res_; } /// Pix vs raw, which to use? Pix is the preferred input for efficiency, /// since raw buffers are copied. /// SetImage for Pix clones its input, so the source pix may be pixDestroyed /// immediately after, but may not go away until after the Thresholder has /// finished with it. void SetImage(const Pix* pix); /// Threshold the source image as efficiently as possible to the output Pix. /// Creates a Pix and sets pix to point to the resulting pointer. /// Caller must use pixDestroy to free the created Pix. virtual void ThresholdToPix(PageSegMode pageseg_mode, Pix** pix); // Gets a pix that contains an 8 bit threshold value at each pixel. The // returned pix may be an integer reduction of the binary image such that // the scale factor may be inferred from the ratio of the sizes, even down // to the extreme of a 1x1 pixel thresholds image. // Ideally the 8 bit threshold should be the exact threshold used to generate // the binary image in ThresholdToPix, but this is not a hard constraint. // Returns NULL if the input is binary. PixDestroy after use. virtual Pix* GetPixRectThresholds(); /// Get a clone/copy of the source image rectangle. /// The returned Pix must be pixDestroyed. /// This function will be used in the future by the page layout analysis, and /// the layout analysis that uses it will only be available with Leptonica, /// so there is no raw equivalent. Pix* GetPixRect(); // Get a clone/copy of the source image rectangle, reduced to greyscale, // and at the same resolution as the output binary. // The returned Pix must be pixDestroyed. // Provided to the classifier to extract features from the greyscale image. virtual Pix* GetPixRectGrey(); protected: // ---------------------------------------------------------------------- // Utility functions that may be useful components for other thresholders. /// Common initialization shared between SetImage methods. virtual void Init(); /// Return true if we are processing the full image. bool IsFullImage() const { return rect_left_ == 0 && rect_top_ == 0 && rect_width_ == image_width_ && rect_height_ == image_height_; } // Otsu thresholds the rectangle, taking the rectangle from *this. void OtsuThresholdRectToPix(Pix* src_pix, Pix** out_pix) const; /// Threshold the rectangle, taking everything except the src_pix /// from the class, using thresholds/hi_values to the output pix. /// NOTE that num_channels is the size of the thresholds and hi_values // arrays and also the bytes per pixel in src_pix. void ThresholdRectToPix(Pix* src_pix, int num_channels, const int* thresholds, const int* hi_values, Pix** pix) const; protected: /// Clone or other copy of the source Pix. /// The pix will always be PixDestroy()ed on destruction of the class. Pix* pix_; int image_width_; //< Width of source pix_. int image_height_; //< Height of source pix_. int pix_channels_; //< Number of 8-bit channels in pix_. int pix_wpl_; //< Words per line of pix_. // Limits of image rectangle to be processed. int scale_; //< Scale factor from original image. int yres_; //< y pixels/inch in source image. int estimated_res_; //< Resolution estimate from text size. int rect_left_; int rect_top_; int rect_width_; int rect_height_; }; } // namespace tesseract. #endif // TESSERACT_CCMAIN_THRESHOLDER_H__ tesseract-3.04.01/ccmain/werdit.cpp000066400000000000000000000046741266071204500171260ustar00rootroot00000000000000/********************************************************************** * File: werdit.cpp (Formerly wordit.c) * Description: An iterator for passing over all the words in a document. * Author: Ray Smith * Created: Mon Apr 27 08:51:22 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. * **********************************************************************/ #include "werdit.h" /********************************************************************** * make_pseudo_word * * Make all the blobs inside a selection into a single word. * The returned PAGE_RES_IT* it points to the new word. After use, call * it->DeleteCurrentWord() to delete the fake word, and then * delete it to get rid of the iterator itself. **********************************************************************/ PAGE_RES_IT* make_pseudo_word(PAGE_RES* page_res, const TBOX& selection_box) { PAGE_RES_IT pr_it(page_res); C_BLOB_LIST new_blobs; // list of gathered blobs C_BLOB_IT new_blob_it = &new_blobs; // iterator for (WERD_RES* word_res = pr_it.word(); word_res != NULL; word_res = pr_it.forward()) { WERD* word = word_res->word; if (word->bounding_box().overlap(selection_box)) { C_BLOB_IT blob_it(word->cblob_list()); for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { C_BLOB* blob = blob_it.data(); if (blob->bounding_box().overlap(selection_box)) { new_blob_it.add_after_then_move(C_BLOB::deep_copy(blob)); } } if (!new_blobs.empty()) { WERD* pseudo_word = new WERD(&new_blobs, 1, NULL); word_res = pr_it.InsertSimpleCloneWord(*word_res, pseudo_word); PAGE_RES_IT* it = new PAGE_RES_IT(page_res); while (it->word() != word_res && it->word() != NULL) it->forward(); ASSERT_HOST(it->word() == word_res); return it; } } } return NULL; } tesseract-3.04.01/ccmain/werdit.h000066400000000000000000000020701266071204500165570ustar00rootroot00000000000000/********************************************************************** * File: wordit.c * Description: An iterator for passing over all the words in a document. * Author: Ray Smith * Created: Mon Apr 27 08:51:22 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 WERDIT_H #define WERDIT_H #include "pageres.h" PAGE_RES_IT* make_pseudo_word(PAGE_RES* page_res, const TBOX& selection_box); #endif tesseract-3.04.01/ccstruct/000077500000000000000000000000001266071204500155115ustar00rootroot00000000000000tesseract-3.04.01/ccstruct/Makefile.am000066400000000000000000000033711266071204500175510ustar00rootroot00000000000000AM_CPPFLAGS += \ -I$(top_srcdir)/ccutil -I$(top_srcdir)/cutil \ -I$(top_srcdir)/viewer \ -I$(top_srcdir)/opencl AM_CPPFLAGS += $(OPENCL_CPPFLAGS) if VISIBILITY AM_CPPFLAGS += -DTESS_EXPORTS \ -fvisibility=hidden -fvisibility-inlines-hidden endif include_HEADERS = publictypes.h noinst_HEADERS = \ 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 \ imagedata.h \ ipoints.h \ linlsq.h matrix.h mod128.h normalis.h \ ocrblock.h ocrpara.h ocrrow.h otsuthr.h \ pageres.h params_training_featdef.h \ pdblock.h points.h polyaprx.h polyblk.h \ quadlsq.h quadratc.h quspline.h ratngs.h rect.h rejctmap.h \ seam.h split.h statistc.h stepblob.h vecfuncs.h werd.h if !USING_MULTIPLELIBS noinst_LTLIBRARIES = libtesseract_ccstruct.la else lib_LTLIBRARIES = libtesseract_ccstruct.la libtesseract_ccstruct_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) libtesseract_ccstruct_la_LIBADD = \ ../ccutil/libtesseract_ccutil.la \ ../cutil/libtesseract_cutil.la \ ../viewer/libtesseract_viewer.la \ ../opencl/libtesseract_opencl.la endif libtesseract_ccstruct_la_SOURCES = \ blamer.cpp blobbox.cpp blobs.cpp blread.cpp boxread.cpp boxword.cpp ccstruct.cpp coutln.cpp \ detlinefit.cpp dppoint.cpp fontinfo.cpp genblob.cpp \ imagedata.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 \ params_training_featdef.cpp publictypes.cpp \ quadlsq.cpp quspline.cpp ratngs.cpp rect.cpp rejctmap.cpp \ seam.cpp split.cpp statistc.cpp stepblob.cpp \ vecfuncs.cpp werd.cpp tesseract-3.04.01/ccstruct/Makefile.in000066400000000000000000000662331266071204500175700ustar00rootroot00000000000000# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @VISIBILITY_TRUE@am__append_1 = -DTESS_EXPORTS \ @VISIBILITY_TRUE@ -fvisibility=hidden -fvisibility-inlines-hidden subdir = ccstruct DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/config/depcomp $(include_HEADERS) \ $(noinst_HEADERS) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config_auto.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)" LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) @USING_MULTIPLELIBS_TRUE@libtesseract_ccstruct_la_DEPENDENCIES = \ @USING_MULTIPLELIBS_TRUE@ ../ccutil/libtesseract_ccutil.la \ @USING_MULTIPLELIBS_TRUE@ ../cutil/libtesseract_cutil.la \ @USING_MULTIPLELIBS_TRUE@ ../viewer/libtesseract_viewer.la \ @USING_MULTIPLELIBS_TRUE@ ../opencl/libtesseract_opencl.la am_libtesseract_ccstruct_la_OBJECTS = blamer.lo blobbox.lo blobs.lo \ blread.lo boxread.lo boxword.lo ccstruct.lo coutln.lo \ detlinefit.lo dppoint.lo fontinfo.lo genblob.lo imagedata.lo \ linlsq.lo matrix.lo mod128.lo normalis.lo ocrblock.lo \ ocrpara.lo ocrrow.lo otsuthr.lo pageres.lo pdblock.lo \ points.lo polyaprx.lo polyblk.lo params_training_featdef.lo \ publictypes.lo quadlsq.lo quspline.lo ratngs.lo rect.lo \ rejctmap.lo seam.lo split.lo statistc.lo stepblob.lo \ vecfuncs.lo werd.lo libtesseract_ccstruct_la_OBJECTS = \ $(am_libtesseract_ccstruct_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libtesseract_ccstruct_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ $(AM_CXXFLAGS) $(CXXFLAGS) $(libtesseract_ccstruct_la_LDFLAGS) \ $(LDFLAGS) -o $@ @USING_MULTIPLELIBS_FALSE@am_libtesseract_ccstruct_la_rpath = @USING_MULTIPLELIBS_TRUE@am_libtesseract_ccstruct_la_rpath = -rpath \ @USING_MULTIPLELIBS_TRUE@ $(libdir) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__depfiles_maybe = depfiles am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(libtesseract_ccstruct_la_SOURCES) DIST_SOURCES = $(libtesseract_ccstruct_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(include_HEADERS) $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_CPPFLAGS = @AM_CPPFLAGS@ -I$(top_srcdir)/ccutil \ -I$(top_srcdir)/cutil -I$(top_srcdir)/viewer \ -I$(top_srcdir)/opencl $(OPENCL_CPPFLAGS) $(am__append_1) AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AM_LDFLAGS = @AM_LDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FRAMEWORK_OPENCL = @FRAMEWORK_OPENCL@ GENERIC_API_VERSION = @GENERIC_API_VERSION@ GENERIC_LIBRARY_NAME = @GENERIC_LIBRARY_NAME@ GENERIC_LIBRARY_VERSION = @GENERIC_LIBRARY_VERSION@ GENERIC_MAJOR_VERSION = @GENERIC_MAJOR_VERSION@ GENERIC_RELEASE = @GENERIC_RELEASE@ GENERIC_VERSION = @GENERIC_VERSION@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBLEPT_HEADERSDIR = @LIBLEPT_HEADERSDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCL_CPPFLAGS = @OPENCL_CPPFLAGS@ OPENCL_LDFLAGS = @OPENCL_LDFLAGS@ OPENMP_CXXFLAGS = @OPENMP_CXXFLAGS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_DATE = @PACKAGE_DATE@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_YEAR = @PACKAGE_YEAR@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ include_HEADERS = publictypes.h noinst_HEADERS = \ 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 \ imagedata.h \ ipoints.h \ linlsq.h matrix.h mod128.h normalis.h \ ocrblock.h ocrpara.h ocrrow.h otsuthr.h \ pageres.h params_training_featdef.h \ pdblock.h points.h polyaprx.h polyblk.h \ quadlsq.h quadratc.h quspline.h ratngs.h rect.h rejctmap.h \ seam.h split.h statistc.h stepblob.h vecfuncs.h werd.h @USING_MULTIPLELIBS_FALSE@noinst_LTLIBRARIES = libtesseract_ccstruct.la @USING_MULTIPLELIBS_TRUE@lib_LTLIBRARIES = libtesseract_ccstruct.la @USING_MULTIPLELIBS_TRUE@libtesseract_ccstruct_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) @USING_MULTIPLELIBS_TRUE@libtesseract_ccstruct_la_LIBADD = \ @USING_MULTIPLELIBS_TRUE@ ../ccutil/libtesseract_ccutil.la \ @USING_MULTIPLELIBS_TRUE@ ../cutil/libtesseract_cutil.la \ @USING_MULTIPLELIBS_TRUE@ ../viewer/libtesseract_viewer.la \ @USING_MULTIPLELIBS_TRUE@ ../opencl/libtesseract_opencl.la libtesseract_ccstruct_la_SOURCES = \ blamer.cpp blobbox.cpp blobs.cpp blread.cpp boxread.cpp boxword.cpp ccstruct.cpp coutln.cpp \ detlinefit.cpp dppoint.cpp fontinfo.cpp genblob.cpp \ imagedata.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 \ params_training_featdef.cpp publictypes.cpp \ quadlsq.cpp quspline.cpp ratngs.cpp rect.cpp rejctmap.cpp \ seam.cpp split.cpp statistc.cpp stepblob.cpp \ vecfuncs.cpp werd.cpp all: all-am .SUFFIXES: .SUFFIXES: .cpp .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign ccstruct/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign ccstruct/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libtesseract_ccstruct.la: $(libtesseract_ccstruct_la_OBJECTS) $(libtesseract_ccstruct_la_DEPENDENCIES) $(EXTRA_libtesseract_ccstruct_la_DEPENDENCIES) $(AM_V_CXXLD)$(libtesseract_ccstruct_la_LINK) $(am_libtesseract_ccstruct_la_rpath) $(libtesseract_ccstruct_la_OBJECTS) $(libtesseract_ccstruct_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blamer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blobbox.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blobs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blread.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/boxread.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/boxword.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ccstruct.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/coutln.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/detlinefit.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dppoint.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fontinfo.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/genblob.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imagedata.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/linlsq.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/matrix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod128.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/normalis.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocrblock.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocrpara.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocrrow.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/otsuthr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pageres.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/params_training_featdef.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdblock.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/points.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polyaprx.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polyblk.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/publictypes.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quadlsq.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quspline.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ratngs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rect.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rejctmap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/seam.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/split.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/statistc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stepblob.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vecfuncs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/werd.Plo@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-includeHEADERS: $(include_HEADERS) @$(NORMAL_INSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ done uninstall-includeHEADERS: @$(NORMAL_UNINSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ clean-noinstLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-includeHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-libLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libLTLIBRARIES clean-libtool clean-noinstLTLIBRARIES \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-includeHEADERS install-info install-info-am \ install-libLTLIBRARIES install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-includeHEADERS \ uninstall-libLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: tesseract-3.04.01/ccstruct/blamer.cpp000066400000000000000000000577211266071204500174730ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // 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()); } } } tesseract-3.04.01/ccstruct/blamer.h000066400000000000000000000341671266071204500171370ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // 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_ tesseract-3.04.01/ccstruct/blckerr.h000066400000000000000000000024101266071204500173030ustar00rootroot00000000000000/********************************************************************** * File: blckerr.h (Formerly blockerr.h) * Description: Error codes for the page block classes. * Author: Ray Smith * Created: Tue Mar 19 17:43:30 GMT 1991 * * (C) Copyright 1991, 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 BLCKERR_H #define BLCKERR_H #include "errcode.h" const ERRCODE BADBLOCKLINE = "Y coordinate in block out of bounds"; const ERRCODE LOSTBLOCKLINE = "Can't find rectangle for line"; const ERRCODE ILLEGAL_GRADIENT = "Gradient wrong side of edge step!"; const ERRCODE WRONG_WORD = "Word doesn't have blobs of that type"; #endif tesseract-3.04.01/ccstruct/blobbox.cpp000066400000000000000000001147211266071204500176520ustar00rootroot00000000000000/********************************************************************** * File: blobbox.cpp (Formerly blobnbox.c) * Description: Code for the textord blob class. * Author: Ray Smith * Created: Thu Jul 30 09:08:51 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. * **********************************************************************/ // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "blobbox.h" #include "allheaders.h" #include "blobs.h" #include "helpers.h" #include "normalis.h" #define PROJECTION_MARGIN 10 //arbitrary #define EXTERN ELISTIZE (BLOBNBOX) ELIST2IZE (TO_ROW) ELISTIZE (TO_BLOCK) // Up to 30 degrees is allowed for rotations of diacritic blobs. const double kCosSmallAngle = 0.866; // Min aspect ratio for a joined word to indicate an obvious flow direction. const double kDefiniteAspectRatio = 2.0; // Multiple of short length in perimeter to make a joined word. const double kComplexShapePerimeterRatio = 1.5; // Min multiple of linesize for medium-sized blobs in ReFilterBlobs. const double kMinMediumSizeRatio = 0.25; // Max multiple of linesize for medium-sized blobs in ReFilterBlobs. const double kMaxMediumSizeRatio = 4.0; // Rotates the box and the underlying blob. void BLOBNBOX::rotate(FCOORD rotation) { cblob_ptr->rotate(rotation); rotate_box(rotation); compute_bounding_box(); } // Reflect the box in the y-axis, leaving the underlying blob untouched. void BLOBNBOX::reflect_box_in_y_axis() { int left = -box.right(); box.set_right(-box.left()); box.set_left(left); } // Rotates the box by the angle given by rotation. // If the blob is a diacritic, then only small rotations for skew // correction can be applied. void BLOBNBOX::rotate_box(FCOORD rotation) { if (IsDiacritic()) { ASSERT_HOST(rotation.x() >= kCosSmallAngle) ICOORD top_pt((box.left() + box.right()) / 2, base_char_top_); ICOORD bottom_pt(top_pt.x(), base_char_bottom_); top_pt.rotate(rotation); base_char_top_ = top_pt.y(); bottom_pt.rotate(rotation); base_char_bottom_ = bottom_pt.y(); box.rotate(rotation); } else { box.rotate(rotation); set_diacritic_box(box); } } /********************************************************************** * BLOBNBOX::merge * * Merge this blob with the given blob, which should be after this. **********************************************************************/ void BLOBNBOX::merge( //merge blobs BLOBNBOX *nextblob //blob to join with ) { box += nextblob->box; //merge boxes set_diacritic_box(box); nextblob->joined = TRUE; } // Merge this with other, taking the outlines from other. // Other is not deleted, but left for the caller to handle. void BLOBNBOX::really_merge(BLOBNBOX* other) { if (cblob_ptr != NULL && other->cblob_ptr != NULL) { C_OUTLINE_IT ol_it(cblob_ptr->out_list()); ol_it.add_list_after(other->cblob_ptr->out_list()); } compute_bounding_box(); } /********************************************************************** * BLOBNBOX::chop * * Chop this blob into equal sized pieces using the x height as a guide. * The blob is not actually chopped. Instead, fake blobs are inserted * with the relevant bounding boxes. **********************************************************************/ void BLOBNBOX::chop( //chop blobs BLOBNBOX_IT *start_it, //location of this BLOBNBOX_IT *end_it, //iterator FCOORD rotation, //for landscape float xheight //of line ) { inT16 blobcount; //no of blobs BLOBNBOX *newblob; //fake blob BLOBNBOX *blob; //current blob inT16 blobindex; //number of chop inT16 leftx; //left edge of blob float blobwidth; //width of each float rightx; //right edge to scan float ymin, ymax; //limits of new blob float test_ymin, test_ymax; //limits of part blob ICOORD bl, tr; //corners of box BLOBNBOX_IT blob_it; //blob iterator //get no of chops blobcount = (inT16) floor (box.width () / xheight); if (blobcount > 1 && cblob_ptr != NULL) { //width of each blobwidth = (float) (box.width () + 1) / blobcount; for (blobindex = blobcount - 1, rightx = box.right (); blobindex >= 0; blobindex--, rightx -= blobwidth) { ymin = (float) MAX_INT32; ymax = (float) -MAX_INT32; blob_it = *start_it; do { blob = blob_it.data (); find_cblob_vlimits(blob->cblob_ptr, rightx - blobwidth, rightx, /*rotation, */ test_ymin, test_ymax); blob_it.forward (); UpdateRange(test_ymin, test_ymax, &ymin, &ymax); } while (blob != end_it->data ()); if (ymin < ymax) { leftx = (inT16) floor (rightx - blobwidth); if (leftx < box.left ()) leftx = box.left (); //clip to real box bl = ICOORD (leftx, (inT16) floor (ymin)); tr = ICOORD ((inT16) ceil (rightx), (inT16) ceil (ymax)); if (blobindex == 0) box = TBOX (bl, tr); //change box else { newblob = new BLOBNBOX; //box is all it has newblob->box = TBOX (bl, tr); //stay on current newblob->base_char_top_ = tr.y(); newblob->base_char_bottom_ = bl.y(); end_it->add_after_stay_put (newblob); } } } } } // Returns the box gaps between this and its neighbours_ in an array // indexed by BlobNeighbourDir. void BLOBNBOX::NeighbourGaps(int gaps[BND_COUNT]) const { for (int dir = 0; dir < BND_COUNT; ++dir) { gaps[dir] = MAX_INT16; BLOBNBOX* neighbour = neighbours_[dir]; if (neighbour != NULL) { TBOX n_box = neighbour->bounding_box(); if (dir == BND_LEFT || dir == BND_RIGHT) { gaps[dir] = box.x_gap(n_box); } else { gaps[dir] = box.y_gap(n_box); } } } } // Returns the min and max horizontal and vertical gaps (from NeighbourGaps) // modified so that if the max exceeds the max dimension of the blob, and // the min is less, the max is replaced with the min. // The objective is to catch cases where there is only a single neighbour // and avoid reporting the other gap as a ridiculously large number void BLOBNBOX::MinMaxGapsClipped(int* h_min, int* h_max, int* v_min, int* v_max) const { int max_dimension = MAX(box.width(), box.height()); int gaps[BND_COUNT]; NeighbourGaps(gaps); *h_min = MIN(gaps[BND_LEFT], gaps[BND_RIGHT]); *h_max = MAX(gaps[BND_LEFT], gaps[BND_RIGHT]); if (*h_max > max_dimension && *h_min < max_dimension) *h_max = *h_min; *v_min = MIN(gaps[BND_ABOVE], gaps[BND_BELOW]); *v_max = MAX(gaps[BND_ABOVE], gaps[BND_BELOW]); if (*v_max > max_dimension && *v_min < max_dimension) *v_max = *v_min; } // NULLs out any neighbours that are DeletableNoise to remove references. void BLOBNBOX::CleanNeighbours() { for (int dir = 0; dir < BND_COUNT; ++dir) { BLOBNBOX* neighbour = neighbours_[dir]; if (neighbour != NULL && neighbour->DeletableNoise()) { neighbours_[dir] = NULL; good_stroke_neighbours_[dir] = false; } } } // Returns positive if there is at least one side neighbour that has a similar // stroke width and is not on the other side of a rule line. int BLOBNBOX::GoodTextBlob() const { int score = 0; for (int dir = 0; dir < BND_COUNT; ++dir) { BlobNeighbourDir bnd = static_cast(dir); if (good_stroke_neighbour(bnd)) ++score; } return score; } // Returns the number of side neighbours that are of type BRT_NOISE. int BLOBNBOX::NoisyNeighbours() const { int count = 0; for (int dir = 0; dir < BND_COUNT; ++dir) { BlobNeighbourDir bnd = static_cast(dir); BLOBNBOX* blob = neighbour(bnd); if (blob != NULL && blob->region_type() == BRT_NOISE) ++count; } return count; } // Returns true, and sets vert_possible/horz_possible if the blob has some // feature that makes it individually appear to flow one way. // eg if it has a high aspect ratio, yet has a complex shape, such as a // joined word in Latin, Arabic, or Hindi, rather than being a -, I, l, 1 etc. bool BLOBNBOX::DefiniteIndividualFlow() { if (cblob() == NULL) return false; int box_perimeter = 2 * (box.height() + box.width()); if (box.width() > box.height() * kDefiniteAspectRatio) { // Attempt to distinguish a wide joined word from a dash. // If it is a dash, then its perimeter is approximately // 2 * (box width + stroke width), but more if the outline is noisy, // so perimeter - 2*(box width + stroke width) should be close to zero. // A complex shape such as a joined word should have a much larger value. int perimeter = cblob()->perimeter(); if (vert_stroke_width() > 0 || perimeter <= 0) perimeter -= 2 * vert_stroke_width(); else perimeter -= 4 * cblob()->area() / perimeter; perimeter -= 2 * box.width(); // Use a multiple of the box perimeter as a threshold. if (perimeter > kComplexShapePerimeterRatio * box_perimeter) { set_vert_possible(false); set_horz_possible(true); return true; } } if (box.height() > box.width() * kDefiniteAspectRatio) { // As above, but for a putative vertical word vs a I/1/l. int perimeter = cblob()->perimeter(); if (horz_stroke_width() > 0 || perimeter <= 0) perimeter -= 2 * horz_stroke_width(); else perimeter -= 4 * cblob()->area() / perimeter; perimeter -= 2 * box.height(); if (perimeter > kComplexShapePerimeterRatio * box_perimeter) { set_vert_possible(true); set_horz_possible(false); return true; } } return false; } // Returns true if there is no tabstop violation in merging this and other. bool BLOBNBOX::ConfirmNoTabViolation(const BLOBNBOX& other) const { if (box.left() < other.box.left() && box.left() < other.left_rule_) return false; if (other.box.left() < box.left() && other.box.left() < left_rule_) return false; if (box.right() > other.box.right() && box.right() > other.right_rule_) return false; if (other.box.right() > box.right() && other.box.right() > right_rule_) return false; return true; } // Returns true if other has a similar stroke width to this. bool BLOBNBOX::MatchingStrokeWidth(const BLOBNBOX& other, double fractional_tolerance, double constant_tolerance) const { // The perimeter-based width is used as a backup in case there is // no information in the blob. double p_width = area_stroke_width(); double n_p_width = other.area_stroke_width(); float h_tolerance = horz_stroke_width_ * fractional_tolerance + constant_tolerance; float v_tolerance = vert_stroke_width_ * fractional_tolerance + constant_tolerance; double p_tolerance = p_width * fractional_tolerance + constant_tolerance; bool h_zero = horz_stroke_width_ == 0.0f || other.horz_stroke_width_ == 0.0f; bool v_zero = vert_stroke_width_ == 0.0f || other.vert_stroke_width_ == 0.0f; bool h_ok = !h_zero && NearlyEqual(horz_stroke_width_, other.horz_stroke_width_, h_tolerance); bool v_ok = !v_zero && NearlyEqual(vert_stroke_width_, other.vert_stroke_width_, v_tolerance); bool p_ok = h_zero && v_zero && NearlyEqual(p_width, n_p_width, p_tolerance); // For a match, at least one of the horizontal and vertical widths // must match, and the other one must either match or be zero. // Only if both are zero will we look at the perimeter metric. return p_ok || ((v_ok || h_ok) && (h_ok || h_zero) && (v_ok || v_zero)); } // Returns a bounding box of the outline contained within the // given horizontal range. TBOX BLOBNBOX::BoundsWithinLimits(int left, int right) { FCOORD no_rotation(1.0f, 0.0f); float top = box.top(); float bottom = box.bottom(); if (cblob_ptr != NULL) { find_cblob_limits(cblob_ptr, static_cast(left), static_cast(right), no_rotation, bottom, top); } if (top < bottom) { top = box.top(); bottom = box.bottom(); } FCOORD bot_left(left, bottom); FCOORD top_right(right, top); TBOX shrunken_box(bot_left); TBOX shrunken_box2(top_right); shrunken_box += shrunken_box2; return shrunken_box; } // Estimates and stores the baseline position based on the shape of the // outline. void BLOBNBOX::EstimateBaselinePosition() { baseline_y_ = box.bottom(); // The default. if (cblob_ptr == NULL) return; baseline_y_ = cblob_ptr->EstimateBaselinePosition(); } // Helper to call CleanNeighbours on all blobs on the list. void BLOBNBOX::CleanNeighbours(BLOBNBOX_LIST* blobs) { BLOBNBOX_IT blob_it(blobs); for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { blob_it.data()->CleanNeighbours(); } } // Helper to delete all the deletable blobs on the list. void BLOBNBOX::DeleteNoiseBlobs(BLOBNBOX_LIST* blobs) { BLOBNBOX_IT blob_it(blobs); for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { BLOBNBOX* blob = blob_it.data(); if (blob->DeletableNoise()) { delete blob->cblob(); delete blob_it.extract(); } } } // Helper to compute edge offsets for all the blobs on the list. // See coutln.h for an explanation of edge offsets. void BLOBNBOX::ComputeEdgeOffsets(Pix* thresholds, Pix* grey, BLOBNBOX_LIST* blobs) { int grey_height = 0; int thr_height = 0; int scale_factor = 1; if (thresholds != NULL && grey != NULL) { grey_height = pixGetHeight(grey); thr_height = pixGetHeight(thresholds); scale_factor = IntCastRounded(static_cast(grey_height) / thr_height); } BLOBNBOX_IT blob_it(blobs); for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { BLOBNBOX* blob = blob_it.data(); if (blob->cblob() != NULL) { // Get the threshold that applies to this blob. l_uint32 threshold = 128; if (thresholds != NULL && grey != NULL) { const TBOX& box = blob->cblob()->bounding_box(); // Transform the coordinates if required. TPOINT pt((box.left() + box.right()) / 2, (box.top() + box.bottom()) / 2); pixGetPixel(thresholds, pt.x / scale_factor, thr_height - 1 - pt.y / scale_factor, &threshold); } blob->cblob()->ComputeEdgeOffsets(threshold, grey); } } } #ifndef GRAPHICS_DISABLED // Helper to draw all the blobs on the list in the given body_colour, // with child outlines in the child_colour. void BLOBNBOX::PlotBlobs(BLOBNBOX_LIST* list, ScrollView::Color body_colour, ScrollView::Color child_colour, ScrollView* win) { BLOBNBOX_IT it(list); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { it.data()->plot(win, body_colour, child_colour); } } // Helper to draw only DeletableNoise blobs (unowned, BRT_NOISE) on the // given list in the given body_colour, with child outlines in the // child_colour. void BLOBNBOX::PlotNoiseBlobs(BLOBNBOX_LIST* list, ScrollView::Color body_colour, ScrollView::Color child_colour, ScrollView* win) { BLOBNBOX_IT it(list); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { BLOBNBOX* blob = it.data(); if (blob->DeletableNoise()) blob->plot(win, body_colour, child_colour); } } ScrollView::Color BLOBNBOX::TextlineColor(BlobRegionType region_type, BlobTextFlowType flow_type) { switch (region_type) { case BRT_HLINE: return ScrollView::BROWN; case BRT_VLINE: return ScrollView::DARK_GREEN; case BRT_RECTIMAGE: return ScrollView::RED; case BRT_POLYIMAGE: return ScrollView::ORANGE; case BRT_UNKNOWN: return flow_type == BTFT_NONTEXT ? ScrollView::CYAN : ScrollView::WHITE; case BRT_VERT_TEXT: if (flow_type == BTFT_STRONG_CHAIN || flow_type == BTFT_TEXT_ON_IMAGE) return ScrollView::GREEN; if (flow_type == BTFT_CHAIN) return ScrollView::LIME_GREEN; return ScrollView::YELLOW; case BRT_TEXT: if (flow_type == BTFT_STRONG_CHAIN) return ScrollView::BLUE; if (flow_type == BTFT_TEXT_ON_IMAGE) return ScrollView::LIGHT_BLUE; if (flow_type == BTFT_CHAIN) return ScrollView::MEDIUM_BLUE; if (flow_type == BTFT_LEADER) return ScrollView::WHEAT; if (flow_type == BTFT_NONTEXT) return ScrollView::PINK; return ScrollView::MAGENTA; default: return ScrollView::GREY; } } // Keep in sync with BlobRegionType. ScrollView::Color BLOBNBOX::BoxColor() const { return TextlineColor(region_type_, flow_); } void BLOBNBOX::plot(ScrollView* window, // window to draw in ScrollView::Color blob_colour, // for outer bits ScrollView::Color child_colour) { // for holes if (cblob_ptr != NULL) cblob_ptr->plot(window, blob_colour, child_colour); } #endif /********************************************************************** * find_cblob_limits * * Scan the outlines of the cblob to locate the y min and max * between the given x limits. **********************************************************************/ void find_cblob_limits( //get y limits C_BLOB *blob, //blob to search float leftx, //x limits float rightx, FCOORD rotation, //for landscape float &ymin, //output y limits float &ymax) { inT16 stepindex; //current point ICOORD pos; //current coords ICOORD vec; //rotated step C_OUTLINE *outline; //current outline //outlines C_OUTLINE_IT out_it = blob->out_list (); ymin = (float) MAX_INT32; ymax = (float) -MAX_INT32; for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { outline = out_it.data (); pos = outline->start_pos (); //get coords pos.rotate (rotation); for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) { //inside if (pos.x () >= leftx && pos.x () <= rightx) { UpdateRange(pos.y(), &ymin, &ymax); } vec = outline->step (stepindex); vec.rotate (rotation); pos += vec; //move to next } } } /********************************************************************** * find_cblob_vlimits * * Scan the outlines of the cblob to locate the y min and max * between the given x limits. **********************************************************************/ void find_cblob_vlimits( //get y limits C_BLOB *blob, //blob to search float leftx, //x limits float rightx, float &ymin, //output y limits float &ymax) { inT16 stepindex; //current point ICOORD pos; //current coords ICOORD vec; //rotated step C_OUTLINE *outline; //current outline //outlines C_OUTLINE_IT out_it = blob->out_list (); ymin = (float) MAX_INT32; ymax = (float) -MAX_INT32; for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { outline = out_it.data (); pos = outline->start_pos (); //get coords for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) { //inside if (pos.x () >= leftx && pos.x () <= rightx) { UpdateRange(pos.y(), &ymin, &ymax); } vec = outline->step (stepindex); pos += vec; //move to next } } } /********************************************************************** * find_cblob_hlimits * * Scan the outlines of the cblob to locate the x min and max * between the given y limits. **********************************************************************/ void find_cblob_hlimits( //get x limits C_BLOB *blob, //blob to search float bottomy, //y limits float topy, float &xmin, //output x limits float &xmax) { inT16 stepindex; //current point ICOORD pos; //current coords ICOORD vec; //rotated step C_OUTLINE *outline; //current outline //outlines C_OUTLINE_IT out_it = blob->out_list (); xmin = (float) MAX_INT32; xmax = (float) -MAX_INT32; for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { outline = out_it.data (); pos = outline->start_pos (); //get coords for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) { //inside if (pos.y () >= bottomy && pos.y () <= topy) { UpdateRange(pos.x(), &xmin, &xmax); } vec = outline->step (stepindex); pos += vec; //move to next } } } /********************************************************************** * crotate_cblob * * Rotate the copy by the given vector and return a C_BLOB. **********************************************************************/ C_BLOB *crotate_cblob( //rotate it C_BLOB *blob, //blob to search FCOORD rotation //for landscape ) { C_OUTLINE_LIST out_list; //output outlines //input outlines C_OUTLINE_IT in_it = blob->out_list (); //output outlines C_OUTLINE_IT out_it = &out_list; for (in_it.mark_cycle_pt (); !in_it.cycled_list (); in_it.forward ()) { out_it.add_after_then_move (new C_OUTLINE (in_it.data (), rotation)); } return new C_BLOB (&out_list); } /********************************************************************** * box_next * * Compute the bounding box of this blob with merging of x overlaps * but no pre-chopping. * Then move the iterator on to the start of the next blob. **********************************************************************/ TBOX box_next( //get bounding box BLOBNBOX_IT *it //iterator to blobds ) { BLOBNBOX *blob; //current blob TBOX result; //total box blob = it->data (); result = blob->bounding_box (); do { it->forward (); blob = it->data (); if (blob->cblob() == NULL) //was pre-chopped result += blob->bounding_box (); } //until next real blob while ((blob->cblob() == NULL) || blob->joined_to_prev()); return result; } /********************************************************************** * box_next_pre_chopped * * Compute the bounding box of this blob with merging of x overlaps * but WITH pre-chopping. * Then move the iterator on to the start of the next pre-chopped blob. **********************************************************************/ TBOX box_next_pre_chopped( //get bounding box BLOBNBOX_IT *it //iterator to blobds ) { BLOBNBOX *blob; //current blob TBOX result; //total box blob = it->data (); result = blob->bounding_box (); do { it->forward (); blob = it->data (); } //until next real blob while (blob->joined_to_prev ()); return result; } /********************************************************************** * TO_ROW::TO_ROW * * Constructor to make a row from a blob. **********************************************************************/ TO_ROW::TO_ROW ( //constructor BLOBNBOX * blob, //first blob float top, //corrected top float bottom, //of row float row_size //ideal ) { clear(); y_min = bottom; y_max = top; initial_y_min = bottom; float diff; //in size BLOBNBOX_IT it = &blobs; //list of blobs it.add_to_end (blob); diff = top - bottom - row_size; if (diff > 0) { y_max -= diff / 2; y_min += diff / 2; } //very small object else if ((top - bottom) * 3 < row_size) { diff = row_size / 3 + bottom - top; y_max += diff / 2; y_min -= diff / 2; } } void TO_ROW::print() const { tprintf("pitch=%d, fp=%g, fps=%g, fpns=%g, prs=%g, prns=%g," " spacing=%g xh=%g y_origin=%g xev=%d, asc=%g, desc=%g," " body=%g, minsp=%d maxnsp=%d, thr=%d kern=%g sp=%g\n", pitch_decision, fixed_pitch, fp_space, fp_nonsp, pr_space, pr_nonsp, spacing, xheight, y_origin, xheight_evidence, ascrise, descdrop, body_size, min_space, max_nonspace, space_threshold, kern_size, space_size); } /********************************************************************** * TO_ROW:add_blob * * Add the blob to the end of the row. **********************************************************************/ void TO_ROW::add_blob( //constructor BLOBNBOX *blob, //first blob float top, //corrected top float bottom, //of row float row_size //ideal ) { float allowed; //allowed expansion float available; //expansion BLOBNBOX_IT it = &blobs; //list of blobs it.add_to_end (blob); allowed = row_size + y_min - y_max; if (allowed > 0) { available = top > y_max ? top - y_max : 0; if (bottom < y_min) //total available available += y_min - bottom; if (available > 0) { available += available; //do it gradually if (available < allowed) available = allowed; if (bottom < y_min) y_min -= (y_min - bottom) * allowed / available; if (top > y_max) y_max += (top - y_max) * allowed / available; } } } /********************************************************************** * TO_ROW:insert_blob * * Add the blob to the row in the correct position. **********************************************************************/ void TO_ROW::insert_blob( //constructor BLOBNBOX *blob //first blob ) { BLOBNBOX_IT it = &blobs; //list of blobs if (it.empty ()) it.add_before_then_move (blob); else { it.mark_cycle_pt (); while (!it.cycled_list () && it.data ()->bounding_box ().left () <= blob->bounding_box ().left ()) it.forward (); if (it.cycled_list ()) it.add_to_end (blob); else it.add_before_stay_put (blob); } } /********************************************************************** * TO_ROW::compute_vertical_projection * * Compute the vertical projection of a TO_ROW from its blobs. **********************************************************************/ void TO_ROW::compute_vertical_projection() { //project whole row TBOX row_box; //bound of row BLOBNBOX *blob; //current blob TBOX blob_box; //bounding box BLOBNBOX_IT blob_it = blob_list (); if (blob_it.empty ()) return; row_box = blob_it.data ()->bounding_box (); for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) row_box += blob_it.data ()->bounding_box (); projection.set_range (row_box.left () - PROJECTION_MARGIN, row_box.right () + PROJECTION_MARGIN); projection_left = row_box.left () - PROJECTION_MARGIN; projection_right = row_box.right () + PROJECTION_MARGIN; for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { blob = blob_it.data(); if (blob->cblob() != NULL) vertical_cblob_projection(blob->cblob(), &projection); } } /********************************************************************** * TO_ROW::clear * * Zero out all scalar members. **********************************************************************/ void TO_ROW::clear() { all_caps = 0; used_dm_model = 0; projection_left = 0; projection_right = 0; pitch_decision = PITCH_DUNNO; fixed_pitch = 0.0; fp_space = 0.0; fp_nonsp = 0.0; pr_space = 0.0; pr_nonsp = 0.0; spacing = 0.0; xheight = 0.0; xheight_evidence = 0; body_size = 0.0; ascrise = 0.0; descdrop = 0.0; min_space = 0; max_nonspace = 0; space_threshold = 0; kern_size = 0.0; space_size = 0.0; y_min = 0.0; y_max = 0.0; initial_y_min = 0.0; m = 0.0; c = 0.0; error = 0.0; para_c = 0.0; para_error = 0.0; y_origin = 0.0; credibility = 0.0; num_repeated_sets_ = -1; } /********************************************************************** * vertical_cblob_projection * * Compute the vertical projection of a cblob from its outlines * and add to the given STATS. **********************************************************************/ void vertical_cblob_projection( //project outlines C_BLOB *blob, //blob to project STATS *stats //output ) { //outlines of blob C_OUTLINE_IT out_it = blob->out_list (); for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { vertical_coutline_projection (out_it.data (), stats); } } /********************************************************************** * vertical_coutline_projection * * Compute the vertical projection of a outline from its outlines * and add to the given STATS. **********************************************************************/ void vertical_coutline_projection( //project outlines C_OUTLINE *outline, //outline to project STATS *stats //output ) { ICOORD pos; //current point ICOORD step; //edge step inT32 length; //of outline inT16 stepindex; //current step C_OUTLINE_IT out_it = outline->child (); pos = outline->start_pos (); length = outline->pathlength (); for (stepindex = 0; stepindex < length; stepindex++) { step = outline->step (stepindex); if (step.x () > 0) { stats->add (pos.x (), -pos.y ()); } else if (step.x () < 0) { stats->add (pos.x () - 1, pos.y ()); } pos += step; } for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { vertical_coutline_projection (out_it.data (), stats); } } /********************************************************************** * TO_BLOCK::TO_BLOCK * * Constructor to make a TO_BLOCK from a real block. **********************************************************************/ TO_BLOCK::TO_BLOCK( //make a block BLOCK *src_block //real block ) { clear(); block = src_block; } static void clear_blobnboxes(BLOBNBOX_LIST* boxes) { BLOBNBOX_IT it = boxes; // A BLOBNBOX generally doesn't own its blobs, so if they do, you // have to delete them explicitly. for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { BLOBNBOX* box = it.data(); if (box->cblob() != NULL) delete box->cblob(); } } /********************************************************************** * TO_BLOCK::clear * * Zero out all scalar members. **********************************************************************/ void TO_BLOCK::clear() { block = NULL; pitch_decision = PITCH_DUNNO; line_spacing = 0.0; line_size = 0.0; max_blob_size = 0.0; baseline_offset = 0.0; xheight = 0.0; fixed_pitch = 0.0; kern_size = 0.0; space_size = 0.0; min_space = 0; max_nonspace = 0; fp_space = 0.0; fp_nonsp = 0.0; pr_space = 0.0; pr_nonsp = 0.0; key_row = NULL; } TO_BLOCK::~TO_BLOCK() { // Any residual BLOBNBOXes at this stage own their blobs, so delete them. clear_blobnboxes(&blobs); clear_blobnboxes(&underlines); clear_blobnboxes(&noise_blobs); clear_blobnboxes(&small_blobs); clear_blobnboxes(&large_blobs); } // Helper function to divide the input blobs over noise, small, medium // and large lists. Blobs small in height and (small in width or large in width) // go in the noise list. Dash (-) candidates go in the small list, and // medium and large are by height. // SIDE-EFFECT: reset all blobs to initial state by calling Init(). static void SizeFilterBlobs(int min_height, int max_height, BLOBNBOX_LIST* src_list, BLOBNBOX_LIST* noise_list, BLOBNBOX_LIST* small_list, BLOBNBOX_LIST* medium_list, BLOBNBOX_LIST* large_list) { BLOBNBOX_IT noise_it(noise_list); BLOBNBOX_IT small_it(small_list); BLOBNBOX_IT medium_it(medium_list); BLOBNBOX_IT large_it(large_list); for (BLOBNBOX_IT src_it(src_list); !src_it.empty(); src_it.forward()) { BLOBNBOX* blob = src_it.extract(); blob->ReInit(); int width = blob->bounding_box().width(); int height = blob->bounding_box().height(); if (height < min_height && (width < min_height || width > max_height)) noise_it.add_after_then_move(blob); else if (height > max_height) large_it.add_after_then_move(blob); else if (height < min_height) small_it.add_after_then_move(blob); else medium_it.add_after_then_move(blob); } } // Reorganize the blob lists with a different definition of small, medium // and large, compared to the original definition. // Height is still the primary filter key, but medium width blobs of small // height become small, and very wide blobs of small height stay noise, along // with small dot-shaped blobs. void TO_BLOCK::ReSetAndReFilterBlobs() { int min_height = IntCastRounded(kMinMediumSizeRatio * line_size); int max_height = IntCastRounded(kMaxMediumSizeRatio * line_size); BLOBNBOX_LIST noise_list; BLOBNBOX_LIST small_list; BLOBNBOX_LIST medium_list; BLOBNBOX_LIST large_list; SizeFilterBlobs(min_height, max_height, &blobs, &noise_list, &small_list, &medium_list, &large_list); SizeFilterBlobs(min_height, max_height, &large_blobs, &noise_list, &small_list, &medium_list, &large_list); SizeFilterBlobs(min_height, max_height, &small_blobs, &noise_list, &small_list, &medium_list, &large_list); SizeFilterBlobs(min_height, max_height, &noise_blobs, &noise_list, &small_list, &medium_list, &large_list); BLOBNBOX_IT blob_it(&blobs); blob_it.add_list_after(&medium_list); blob_it.set_to_list(&large_blobs); blob_it.add_list_after(&large_list); blob_it.set_to_list(&small_blobs); blob_it.add_list_after(&small_list); blob_it.set_to_list(&noise_blobs); blob_it.add_list_after(&noise_list); } // Deletes noise blobs from all lists where not owned by a ColPartition. void TO_BLOCK::DeleteUnownedNoise() { BLOBNBOX::CleanNeighbours(&blobs); BLOBNBOX::CleanNeighbours(&small_blobs); BLOBNBOX::CleanNeighbours(&noise_blobs); BLOBNBOX::CleanNeighbours(&large_blobs); BLOBNBOX::DeleteNoiseBlobs(&blobs); BLOBNBOX::DeleteNoiseBlobs(&small_blobs); BLOBNBOX::DeleteNoiseBlobs(&noise_blobs); BLOBNBOX::DeleteNoiseBlobs(&large_blobs); } // Computes and stores the edge offsets on each blob for use in feature // extraction, using greyscale if the supplied grey and thresholds pixes // are 8-bit or otherwise (if NULL or not 8 bit) the original binary // edge step outlines. // Thresholds must either be the same size as grey or an integer down-scale // of grey. // See coutln.h for an explanation of edge offsets. void TO_BLOCK::ComputeEdgeOffsets(Pix* thresholds, Pix* grey) { BLOBNBOX::ComputeEdgeOffsets(thresholds, grey, &blobs); BLOBNBOX::ComputeEdgeOffsets(thresholds, grey, &small_blobs); BLOBNBOX::ComputeEdgeOffsets(thresholds, grey, &noise_blobs); } #ifndef GRAPHICS_DISABLED // Draw the noise blobs from all lists in red. void TO_BLOCK::plot_noise_blobs(ScrollView* win) { BLOBNBOX::PlotNoiseBlobs(&noise_blobs, ScrollView::RED, ScrollView::RED, win); BLOBNBOX::PlotNoiseBlobs(&small_blobs, ScrollView::RED, ScrollView::RED, win); BLOBNBOX::PlotNoiseBlobs(&large_blobs, ScrollView::RED, ScrollView::RED, win); BLOBNBOX::PlotNoiseBlobs(&blobs, ScrollView::RED, ScrollView::RED, win); } // Draw the blobs on the various lists in the block in different colors. void TO_BLOCK::plot_graded_blobs(ScrollView* win) { BLOBNBOX::PlotBlobs(&noise_blobs, ScrollView::CORAL, ScrollView::BLUE, win); BLOBNBOX::PlotBlobs(&small_blobs, ScrollView::GOLDENROD, ScrollView::YELLOW, win); BLOBNBOX::PlotBlobs(&large_blobs, ScrollView::DARK_GREEN, ScrollView::YELLOW, win); BLOBNBOX::PlotBlobs(&blobs, ScrollView::WHITE, ScrollView::BROWN, win); } /********************************************************************** * plot_blob_list * * Draw a list of blobs. **********************************************************************/ void plot_blob_list(ScrollView* win, // window to draw in BLOBNBOX_LIST *list, // blob list ScrollView::Color body_colour, // colour to draw ScrollView::Color child_colour) { // colour of child BLOBNBOX_IT it = list; for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { it.data()->plot(win, body_colour, child_colour); } } #endif // GRAPHICS_DISABLED tesseract-3.04.01/ccstruct/blobbox.h000066400000000000000000000742301266071204500173170ustar00rootroot00000000000000/********************************************************************** * File: blobbox.h (Formerly blobnbox.h) * Description: Code for the textord blob class. * Author: Ray Smith * Created: Thu Jul 30 09:08:51 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 BLOBBOX_H #define BLOBBOX_H #include "clst.h" #include "elst2.h" #include "werd.h" #include "ocrblock.h" #include "statistc.h" enum PITCH_TYPE { PITCH_DUNNO, // insufficient data PITCH_DEF_FIXED, // definitely fixed PITCH_MAYBE_FIXED, // could be PITCH_DEF_PROP, PITCH_MAYBE_PROP, PITCH_CORR_FIXED, PITCH_CORR_PROP }; // The possible tab-stop types of each side of a BLOBNBOX. // The ordering is important, as it is used for deleting dead-ends in the // search. ALIGNED, CONFIRMED and VLINE should remain greater than the // non-aligned, unset, or deleted members. enum TabType { TT_NONE, // Not a tab. TT_DELETED, // Not a tab after detailed analysis. TT_MAYBE_RAGGED, // Initial designation of a tab-stop candidate. TT_MAYBE_ALIGNED, // Initial designation of a tab-stop candidate. TT_CONFIRMED, // Aligned with neighbours. TT_VLINE // Detected as a vertical line. }; // The possible region types of a BLOBNBOX. // Note: keep all the text types > BRT_UNKNOWN and all the image types less. // Keep in sync with kBlobTypes in colpartition.cpp and BoxColor, and the // *Type static functions below. enum BlobRegionType { BRT_NOISE, // Neither text nor image. BRT_HLINE, // Horizontal separator line. BRT_VLINE, // Vertical separator line. BRT_RECTIMAGE, // Rectangular image. BRT_POLYIMAGE, // Non-rectangular image. BRT_UNKNOWN, // Not determined yet. BRT_VERT_TEXT, // Vertical alignment, not necessarily vertically oriented. BRT_TEXT, // Convincing text. BRT_COUNT // Number of possibilities. }; // enum for elements of arrays that refer to neighbours. // NOTE: keep in this order, so ^2 can be used to flip direction. enum BlobNeighbourDir { BND_LEFT, BND_BELOW, BND_RIGHT, BND_ABOVE, BND_COUNT }; // enum for special type of text characters, such as math symbol or italic. enum BlobSpecialTextType { BSTT_NONE, // No special. BSTT_ITALIC, // Italic style. BSTT_DIGIT, // Digit symbols. BSTT_MATH, // Mathmatical symobls (not including digit). BSTT_UNCLEAR, // Characters with low recognition rate. BSTT_SKIP, // Characters that we skip labeling (usually too small). BSTT_COUNT }; inline BlobNeighbourDir DirOtherWay(BlobNeighbourDir dir) { return static_cast(dir ^ 2); } // BlobTextFlowType indicates the quality of neighbouring information // related to a chain of connected components, either horizontally or // vertically. Also used by ColPartition for the collection of blobs // within, which should all have the same value in most cases. enum BlobTextFlowType { BTFT_NONE, // No text flow set yet. BTFT_NONTEXT, // Flow too poor to be likely text. BTFT_NEIGHBOURS, // Neighbours support flow in this direction. BTFT_CHAIN, // There is a weak chain of text in this direction. BTFT_STRONG_CHAIN, // There is a strong chain of text in this direction. BTFT_TEXT_ON_IMAGE, // There is a strong chain of text on an image. BTFT_LEADER, // Leader dots/dashes etc. BTFT_COUNT }; // Returns true if type1 dominates type2 in a merge. Mostly determined by the // ordering of the enum, LEADER is weak and dominates nothing. // The function is anti-symmetric (t1 > t2) === !(t2 > t1), except that // this cannot be true if t1 == t2, so the result is undefined. inline bool DominatesInMerge(BlobTextFlowType type1, BlobTextFlowType type2) { // LEADER always loses. if (type1 == BTFT_LEADER) return false; if (type2 == BTFT_LEADER) return true; // With those out of the way, the ordering of the enum determines the result. return type1 >= type2; } namespace tesseract { class ColPartition; } class BLOBNBOX; ELISTIZEH (BLOBNBOX) class BLOBNBOX:public ELIST_LINK { public: BLOBNBOX() { ConstructionInit(); } explicit BLOBNBOX(C_BLOB *srcblob) { box = srcblob->bounding_box(); ConstructionInit(); cblob_ptr = srcblob; area = static_cast(srcblob->area()); } ~BLOBNBOX() { if (owns_cblob_) delete cblob_ptr; } static BLOBNBOX* RealBlob(C_OUTLINE* outline) { C_BLOB* blob = new C_BLOB(outline); return new BLOBNBOX(blob); } // Rotates the box and the underlying blob. void rotate(FCOORD rotation); // Methods that act on the box without touching the underlying blob. // Reflect the box in the y-axis, leaving the underlying blob untouched. void reflect_box_in_y_axis(); // Rotates the box by the angle given by rotation. // If the blob is a diacritic, then only small rotations for skew // correction can be applied. void rotate_box(FCOORD rotation); // Moves just the box by the given vector. void translate_box(ICOORD v) { if (IsDiacritic()) { box.move(v); base_char_top_ += v.y(); base_char_bottom_ += v.y(); } else { box.move(v); set_diacritic_box(box); } } void merge(BLOBNBOX *nextblob); void really_merge(BLOBNBOX* other); void chop( // fake chop blob BLOBNBOX_IT *start_it, // location of this BLOBNBOX_IT *blob_it, // iterator FCOORD rotation, // for landscape float xheight); // line height void NeighbourGaps(int gaps[BND_COUNT]) const; void MinMaxGapsClipped(int* h_min, int* h_max, int* v_min, int* v_max) const; void CleanNeighbours(); // Returns positive if there is at least one side neighbour that has a // similar stroke width and is not on the other side of a rule line. int GoodTextBlob() const; // Returns the number of side neighbours that are of type BRT_NOISE. int NoisyNeighbours() const; // Returns true if the blob is noise and has no owner. bool DeletableNoise() const { return owner() == NULL && region_type() == BRT_NOISE; } // Returns true, and sets vert_possible/horz_possible if the blob has some // feature that makes it individually appear to flow one way. // eg if it has a high aspect ratio, yet has a complex shape, such as a // joined word in Latin, Arabic, or Hindi, rather than being a -, I, l, 1. bool DefiniteIndividualFlow(); // Returns true if there is no tabstop violation in merging this and other. bool ConfirmNoTabViolation(const BLOBNBOX& other) const; // Returns true if other has a similar stroke width to this. bool MatchingStrokeWidth(const BLOBNBOX& other, double fractional_tolerance, double constant_tolerance) const; // Returns a bounding box of the outline contained within the // given horizontal range. TBOX BoundsWithinLimits(int left, int right); // Estimates and stores the baseline position based on the shape of the // outline. void EstimateBaselinePosition(); // Simple accessors. const TBOX& bounding_box() const { return box; } // Set the bounding box. Use with caution. // Normally use compute_bounding_box instead. void set_bounding_box(const TBOX& new_box) { box = new_box; base_char_top_ = box.top(); base_char_bottom_ = box.bottom(); } void compute_bounding_box() { box = cblob_ptr->bounding_box(); base_char_top_ = box.top(); base_char_bottom_ = box.bottom(); baseline_y_ = box.bottom(); } const TBOX& reduced_box() const { return red_box; } void set_reduced_box(TBOX new_box) { red_box = new_box; reduced = TRUE; } inT32 enclosed_area() const { return area; } bool joined_to_prev() const { return joined != 0; } bool red_box_set() const { return reduced != 0; } int repeated_set() const { return repeated_set_; } void set_repeated_set(int set_id) { repeated_set_ = set_id; } C_BLOB *cblob() const { return cblob_ptr; } TabType left_tab_type() const { return left_tab_type_; } void set_left_tab_type(TabType new_type) { left_tab_type_ = new_type; } TabType right_tab_type() const { return right_tab_type_; } void set_right_tab_type(TabType new_type) { right_tab_type_ = new_type; } BlobRegionType region_type() const { return region_type_; } void set_region_type(BlobRegionType new_type) { region_type_ = new_type; } BlobSpecialTextType special_text_type() const { return spt_type_; } void set_special_text_type(BlobSpecialTextType new_type) { spt_type_ = new_type; } BlobTextFlowType flow() const { return flow_; } void set_flow(BlobTextFlowType value) { flow_ = value; } bool vert_possible() const { return vert_possible_; } void set_vert_possible(bool value) { vert_possible_ = value; } bool horz_possible() const { return horz_possible_; } void set_horz_possible(bool value) { horz_possible_ = value; } int left_rule() const { return left_rule_; } void set_left_rule(int new_left) { left_rule_ = new_left; } int right_rule() const { return right_rule_; } void set_right_rule(int new_right) { right_rule_ = new_right; } int left_crossing_rule() const { return left_crossing_rule_; } void set_left_crossing_rule(int new_left) { left_crossing_rule_ = new_left; } int right_crossing_rule() const { return right_crossing_rule_; } void set_right_crossing_rule(int new_right) { right_crossing_rule_ = new_right; } float horz_stroke_width() const { return horz_stroke_width_; } void set_horz_stroke_width(float width) { horz_stroke_width_ = width; } float vert_stroke_width() const { return vert_stroke_width_; } void set_vert_stroke_width(float width) { vert_stroke_width_ = width; } float area_stroke_width() const { return area_stroke_width_; } tesseract::ColPartition* owner() const { return owner_; } void set_owner(tesseract::ColPartition* new_owner) { owner_ = new_owner; } bool leader_on_left() const { return leader_on_left_; } void set_leader_on_left(bool flag) { leader_on_left_ = flag; } bool leader_on_right() const { return leader_on_right_; } void set_leader_on_right(bool flag) { leader_on_right_ = flag; } BLOBNBOX* neighbour(BlobNeighbourDir n) const { return neighbours_[n]; } bool good_stroke_neighbour(BlobNeighbourDir n) const { return good_stroke_neighbours_[n]; } void set_neighbour(BlobNeighbourDir n, BLOBNBOX* neighbour, bool good) { neighbours_[n] = neighbour; good_stroke_neighbours_[n] = good; } bool IsDiacritic() const { return base_char_top_ != box.top() || base_char_bottom_ != box.bottom(); } int base_char_top() const { return base_char_top_; } int base_char_bottom() const { return base_char_bottom_; } int baseline_position() const { return baseline_y_; } int line_crossings() const { return line_crossings_; } void set_line_crossings(int value) { line_crossings_ = value; } void set_diacritic_box(const TBOX& diacritic_box) { base_char_top_ = diacritic_box.top(); base_char_bottom_ = diacritic_box.bottom(); } BLOBNBOX* base_char_blob() const { return base_char_blob_; } void set_base_char_blob(BLOBNBOX* blob) { base_char_blob_ = blob; } void set_owns_cblob(bool value) { owns_cblob_ = value; } bool UniquelyVertical() const { return vert_possible_ && !horz_possible_; } bool UniquelyHorizontal() const { return horz_possible_ && !vert_possible_; } // Returns true if the region type is text. static bool IsTextType(BlobRegionType type) { return type == BRT_TEXT || type == BRT_VERT_TEXT; } // Returns true if the region type is image. static bool IsImageType(BlobRegionType type) { return type == BRT_RECTIMAGE || type == BRT_POLYIMAGE; } // Returns true if the region type is line. static bool IsLineType(BlobRegionType type) { return type == BRT_HLINE || type == BRT_VLINE; } // Returns true if the region type cannot be merged. static bool UnMergeableType(BlobRegionType type) { return IsLineType(type) || IsImageType(type); } // Helper to call CleanNeighbours on all blobs on the list. static void CleanNeighbours(BLOBNBOX_LIST* blobs); // Helper to delete all the deletable blobs on the list. static void DeleteNoiseBlobs(BLOBNBOX_LIST* blobs); // Helper to compute edge offsets for all the blobs on the list. // See coutln.h for an explanation of edge offsets. static void ComputeEdgeOffsets(Pix* thresholds, Pix* grey, BLOBNBOX_LIST* blobs); #ifndef GRAPHICS_DISABLED // Helper to draw all the blobs on the list in the given body_colour, // with child outlines in the child_colour. static void PlotBlobs(BLOBNBOX_LIST* list, ScrollView::Color body_colour, ScrollView::Color child_colour, ScrollView* win); // Helper to draw only DeletableNoise blobs (unowned, BRT_NOISE) on the // given list in the given body_colour, with child outlines in the // child_colour. static void PlotNoiseBlobs(BLOBNBOX_LIST* list, ScrollView::Color body_colour, ScrollView::Color child_colour, ScrollView* win); static ScrollView::Color TextlineColor(BlobRegionType region_type, BlobTextFlowType flow_type); // Keep in sync with BlobRegionType. ScrollView::Color BoxColor() const; void plot(ScrollView* window, // window to draw in ScrollView::Color blob_colour, // for outer bits ScrollView::Color child_colour); // for holes #endif // Initializes the bulk of the members to default values for use at // construction time. void ConstructionInit() { cblob_ptr = NULL; owns_cblob_ = false; area = 0; area_stroke_width_ = 0.0f; horz_stroke_width_ = 0.0f; vert_stroke_width_ = 0.0f; ReInit(); } // Initializes members set by StrokeWidth and beyond, without discarding // stored area and strokewidth values, which are expensive to calculate. void ReInit() { joined = false; reduced = false; repeated_set_ = 0; left_tab_type_ = TT_NONE; right_tab_type_ = TT_NONE; region_type_ = BRT_UNKNOWN; flow_ = BTFT_NONE; spt_type_ = BSTT_SKIP; left_rule_ = 0; right_rule_ = 0; left_crossing_rule_ = 0; right_crossing_rule_ = 0; if (area_stroke_width_ == 0.0f && area > 0 && cblob() != NULL) area_stroke_width_ = 2.0f * area / cblob()->perimeter(); owner_ = NULL; base_char_top_ = box.top(); base_char_bottom_ = box.bottom(); baseline_y_ = box.bottom(); line_crossings_ = 0; base_char_blob_ = NULL; horz_possible_ = false; vert_possible_ = false; leader_on_left_ = false; leader_on_right_ = false; ClearNeighbours(); } void ClearNeighbours() { for (int n = 0; n < BND_COUNT; ++n) { neighbours_[n] = NULL; good_stroke_neighbours_[n] = false; } } private: C_BLOB *cblob_ptr; // edgestep blob TBOX box; // bounding box TBOX red_box; // bounding box int area:30; // enclosed area int joined:1; // joined to prev int reduced:1; // reduced box set int repeated_set_; // id of the set of repeated blobs TabType left_tab_type_; // Indicates tab-stop assessment TabType right_tab_type_; // Indicates tab-stop assessment BlobRegionType region_type_; // Type of region this blob belongs to BlobTextFlowType flow_; // Quality of text flow. inT16 left_rule_; // x-coord of nearest but not crossing rule line inT16 right_rule_; // x-coord of nearest but not crossing rule line inT16 left_crossing_rule_; // x-coord of nearest or crossing rule line inT16 right_crossing_rule_; // x-coord of nearest or crossing rule line inT16 base_char_top_; // y-coord of top/bottom of diacritic base, inT16 base_char_bottom_; // if it exists else top/bottom of this blob. inT16 baseline_y_; // Estimate of baseline position. int line_crossings_; // Number of line intersections touched. BLOBNBOX* base_char_blob_; // The blob that was the base char. float horz_stroke_width_; // Median horizontal stroke width float vert_stroke_width_; // Median vertical stroke width float area_stroke_width_; // Stroke width from area/perimeter ratio. tesseract::ColPartition* owner_; // Who will delete me when I am not needed BlobSpecialTextType spt_type_; // Special text type. BLOBNBOX* neighbours_[BND_COUNT]; bool good_stroke_neighbours_[BND_COUNT]; bool horz_possible_; // Could be part of horizontal flow. bool vert_possible_; // Could be part of vertical flow. bool leader_on_left_; // There is a leader to the left. bool leader_on_right_; // There is a leader to the right. // Iff true, then the destructor should delete the cblob_ptr. // TODO(rays) migrate all uses to correctly setting this flag instead of // deleting the C_BLOB before deleting the BLOBNBOX. bool owns_cblob_; }; class TO_ROW: public ELIST2_LINK { public: static const int kErrorWeight = 3; TO_ROW() { clear(); } //empty TO_ROW( //constructor BLOBNBOX *blob, //from first blob float top, //of row //target height float bottom, float row_size); void print() const; float max_y() const { //access function return y_max; } float min_y() const { return y_min; } float mean_y() const { return (y_min + y_max) / 2.0f; } float initial_min_y() const { return initial_y_min; } float line_m() const { //access to line fit return m; } float line_c() const { return c; } float line_error() const { return error; } float parallel_c() const { return para_c; } float parallel_error() const { return para_error; } float believability() const { //baseline goodness return credibility; } float intercept() const { //real parallel_c return y_origin; } void add_blob( //put in row BLOBNBOX *blob, //blob to add float top, //of row //target height float bottom, float row_size); void insert_blob( //put in row in order BLOBNBOX *blob); BLOBNBOX_LIST *blob_list() { //get list return &blobs; } void set_line( //set line spec float new_m, //line to set float new_c, float new_error) { m = new_m; c = new_c; error = new_error; } void set_parallel_line( //set fixed gradient line float gradient, //page gradient float new_c, float new_error) { para_c = new_c; para_error = new_error; credibility = (float) (blobs.length () - kErrorWeight * new_error); y_origin = (float) (new_c / sqrt (1 + gradient * gradient)); //real intercept } void set_limits( //set min,max float new_min, //bottom and float new_max) { //top of row y_min = new_min; y_max = new_max; } void compute_vertical_projection(); //get projection bool rep_chars_marked() const { return num_repeated_sets_ != -1; } void clear_rep_chars_marked() { num_repeated_sets_ = -1; } int num_repeated_sets() const { return num_repeated_sets_; } void set_num_repeated_sets(int num_sets) { num_repeated_sets_ = num_sets; } // true when dead BOOL8 merged; BOOL8 all_caps; // had no ascenders BOOL8 used_dm_model; // in guessing pitch inT16 projection_left; // start of projection inT16 projection_right; // start of projection PITCH_TYPE pitch_decision; // how strong is decision float fixed_pitch; // pitch or 0 float fp_space; // sp if fixed pitch float fp_nonsp; // nonsp if fixed pitch float pr_space; // sp if prop float pr_nonsp; // non sp if prop float spacing; // to "next" row float xheight; // of line int xheight_evidence; // number of blobs of height xheight float ascrise; // ascenders float descdrop; // descenders float body_size; // of CJK characters. Assumed to be // xheight+ascrise for non-CJK text. inT32 min_space; // min size for real space inT32 max_nonspace; // max size of non-space inT32 space_threshold; // space vs nonspace float kern_size; // average non-space float space_size; // average space WERD_LIST rep_words; // repeated chars ICOORDELT_LIST char_cells; // fixed pitch cells QSPLINE baseline; // curved baseline STATS projection; // vertical projection private: void clear(); // clear all values to reasonable defaults BLOBNBOX_LIST blobs; //blobs in row float y_min; //coords float y_max; float initial_y_min; float m, c; //line spec float error; //line error float para_c; //constrained fit float para_error; float y_origin; //rotated para_c; float credibility; //baseline believability int num_repeated_sets_; // number of sets of repeated blobs // set to -1 if we have not searched // for repeated blobs in this row yet }; ELIST2IZEH (TO_ROW) class TO_BLOCK:public ELIST_LINK { public: TO_BLOCK() : pitch_decision(PITCH_DUNNO) { clear(); } //empty TO_BLOCK( //constructor BLOCK *src_block); //real block ~TO_BLOCK(); void clear(); // clear all scalar members. TO_ROW_LIST *get_rows() { //access function return &row_list; } // Rotate all the blobnbox lists and the underlying block. Then update the // median size statistic from the blobs list. void rotate(const FCOORD& rotation) { BLOBNBOX_LIST* blobnbox_list[] = {&blobs, &underlines, &noise_blobs, &small_blobs, &large_blobs, NULL}; for (BLOBNBOX_LIST** list = blobnbox_list; *list != NULL; ++list) { BLOBNBOX_IT it(*list); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { it.data()->rotate(rotation); } } // Rotate the block ASSERT_HOST(block->poly_block() != NULL); block->rotate(rotation); // Update the median size statistic from the blobs list. STATS widths(0, block->bounding_box().width()); STATS heights(0, block->bounding_box().height()); BLOBNBOX_IT blob_it(&blobs); for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { widths.add(blob_it.data()->bounding_box().width(), 1); heights.add(blob_it.data()->bounding_box().height(), 1); } block->set_median_size(static_cast(widths.median() + 0.5), static_cast(heights.median() + 0.5)); } void print_rows() { //debug info TO_ROW_IT row_it = &row_list; TO_ROW *row; for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { row = row_it.data(); tprintf("Row range (%g,%g), para_c=%g, blobcount=" INT32FORMAT "\n", row->min_y(), row->max_y(), row->parallel_c(), row->blob_list()->length()); } } // Reorganizes the blob lists with a different definition of small, medium // and large, compared to the original definition. // Height is still the primary filter key, but medium width blobs of small // height become medium, and very wide blobs of small height stay small. void ReSetAndReFilterBlobs(); // Deletes noise blobs from all lists where not owned by a ColPartition. void DeleteUnownedNoise(); // Computes and stores the edge offsets on each blob for use in feature // extraction, using greyscale if the supplied grey and thresholds pixes // are 8-bit or otherwise (if NULL or not 8 bit) the original binary // edge step outlines. // Thresholds must either be the same size as grey or an integer down-scale // of grey. // See coutln.h for an explanation of edge offsets. void ComputeEdgeOffsets(Pix* thresholds, Pix* grey); #ifndef GRAPHICS_DISABLED // Draw the noise blobs from all lists in red. void plot_noise_blobs(ScrollView* to_win); // Draw the blobs on on the various lists in the block in different colors. void plot_graded_blobs(ScrollView* to_win); #endif BLOBNBOX_LIST blobs; //medium size BLOBNBOX_LIST underlines; //underline blobs BLOBNBOX_LIST noise_blobs; //very small BLOBNBOX_LIST small_blobs; //fairly small BLOBNBOX_LIST large_blobs; //big blobs BLOCK *block; //real block PITCH_TYPE pitch_decision; //how strong is decision float line_spacing; //estimate // line_size is a lower-bound estimate of the font size in pixels of // the text in the block (with ascenders and descenders), being a small // (1.25) multiple of the median height of filtered blobs. // In most cases the font size will be bigger, but it will be closer // if the text is allcaps, or in a no-x-height script. float line_size; //estimate float max_blob_size; //line assignment limit float baseline_offset; //phase shift float xheight; //median blob size float fixed_pitch; //pitch or 0 float kern_size; //average non-space float space_size; //average space inT32 min_space; //min definite space inT32 max_nonspace; //max definite float fp_space; //sp if fixed pitch float fp_nonsp; //nonsp if fixed pitch float pr_space; //sp if prop float pr_nonsp; //non sp if prop TO_ROW *key_row; //starting row private: TO_ROW_LIST row_list; //temporary rows }; ELISTIZEH (TO_BLOCK) extern double_VAR_H (textord_error_weight, 3, "Weighting for error in believability"); void find_cblob_limits( //get y limits C_BLOB *blob, //blob to search float leftx, //x limits float rightx, FCOORD rotation, //for landscape float &ymin, //output y limits float &ymax); void find_cblob_vlimits( //get y limits C_BLOB *blob, //blob to search float leftx, //x limits float rightx, float &ymin, //output y limits float &ymax); void find_cblob_hlimits( //get x limits C_BLOB *blob, //blob to search float bottomy, //y limits float topy, float &xmin, //output x limits float &xymax); C_BLOB *crotate_cblob( //rotate it C_BLOB *blob, //blob to search FCOORD rotation //for landscape ); TBOX box_next( //get bounding box BLOBNBOX_IT *it //iterator to blobds ); TBOX box_next_pre_chopped( //get bounding box BLOBNBOX_IT *it //iterator to blobds ); void vertical_cblob_projection( //project outlines C_BLOB *blob, //blob to project STATS *stats //output ); void vertical_coutline_projection( //project outlines C_OUTLINE *outline, //outline to project STATS *stats //output ); #ifndef GRAPHICS_DISABLED void plot_blob_list(ScrollView* win, // window to draw in BLOBNBOX_LIST *list, // blob list ScrollView::Color body_colour, // colour to draw ScrollView::Color child_colour); // colour of child #endif // GRAPHICS_DISABLED #endif tesseract-3.04.01/ccstruct/blobs.cpp000066400000000000000000001126571266071204500173320ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: blobs.c (Formerly blobs.c) * Description: Blob definition * Author: Mark Seaman, OCR Technology * Created: Fri Oct 27 15:39:52 1989 * Modified: Thu Mar 28 15:33:26 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 ----------------------------------------------------------------------*/ // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "blobs.h" #include "ccstruct.h" #include "clst.h" #include "cutil.h" #include "emalloc.h" #include "helpers.h" #include "linlsq.h" #include "ndminx.h" #include "normalis.h" #include "ocrblock.h" #include "ocrrow.h" #include "points.h" #include "polyaprx.h" #include "structures.h" #include "werd.h" using tesseract::CCStruct; // A Vector representing the "vertical" direction when measuring the // divisiblity of blobs into multiple blobs just by separating outlines. // See divisible_blob below for the use. const TPOINT kDivisibleVerticalUpright(0, 1); // A vector representing the "vertical" direction for italic text for use // when separating outlines. Using it actually deteriorates final accuracy, // so it is only used for ApplyBoxes chopping to get a better segmentation. const TPOINT kDivisibleVerticalItalic(1, 5); /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ CLISTIZE(EDGEPT); // Returns true when the two line segments cross each other. // (Moved from outlines.cpp). // Finds where the projected lines would cross and then checks to see if the // point of intersection lies on both of the line segments. If it does // then these two segments cross. /* static */ bool TPOINT::IsCrossed(const TPOINT& a0, const TPOINT& a1, const TPOINT& b0, const TPOINT& b1) { int b0a1xb0b1, b0b1xb0a0; int a1b1xa1a0, a1a0xa1b0; TPOINT b0a1, b0a0, a1b1, b0b1, a1a0; b0a1.x = a1.x - b0.x; b0a0.x = a0.x - b0.x; a1b1.x = b1.x - a1.x; b0b1.x = b1.x - b0.x; a1a0.x = a0.x - a1.x; b0a1.y = a1.y - b0.y; b0a0.y = a0.y - b0.y; a1b1.y = b1.y - a1.y; b0b1.y = b1.y - b0.y; a1a0.y = a0.y - a1.y; b0a1xb0b1 = CROSS(b0a1, b0b1); b0b1xb0a0 = CROSS(b0b1, b0a0); a1b1xa1a0 = CROSS(a1b1, a1a0); // For clarity, we want CROSS(a1a0,a1b0) here but we have b0a1 instead of a1b0 // so use -CROSS(a1b0,b0a1) instead, which is the same. a1a0xa1b0 = -CROSS(a1a0, b0a1); return ((b0a1xb0b1 > 0 && b0b1xb0a0 > 0) || (b0a1xb0b1 < 0 && b0b1xb0a0 < 0)) && ((a1b1xa1a0 > 0 && a1a0xa1b0 > 0) || (a1b1xa1a0 < 0 && a1a0xa1b0 < 0)); } // Consume the circular list of EDGEPTs to make a TESSLINE. TESSLINE* TESSLINE::BuildFromOutlineList(EDGEPT* outline) { TESSLINE* result = new TESSLINE; result->loop = outline; if (outline->src_outline != NULL) { // ASSUMPTION: This function is only ever called from ApproximateOutline // and therefore either all points have a src_outline or all do not. // Just as SetupFromPos sets the vectors from the vertices, setup the // step_count members to indicate the (positive) number of original // C_OUTLINE steps to the next vertex. EDGEPT* pt = outline; do { pt->step_count = pt->next->start_step - pt->start_step; if (pt->step_count < 0) pt->step_count += pt->src_outline->pathlength(); pt = pt->next; } while (pt != outline); } result->SetupFromPos(); return result; } // Copies the data and the outline, but leaves next untouched. void TESSLINE::CopyFrom(const TESSLINE& src) { Clear(); topleft = src.topleft; botright = src.botright; start = src.start; is_hole = src.is_hole; if (src.loop != NULL) { EDGEPT* prevpt = NULL; EDGEPT* newpt = NULL; EDGEPT* srcpt = src.loop; do { newpt = new EDGEPT(*srcpt); if (prevpt == NULL) { loop = newpt; } else { newpt->prev = prevpt; prevpt->next = newpt; } prevpt = newpt; srcpt = srcpt->next; } while (srcpt != src.loop); loop->prev = newpt; newpt->next = loop; } } // Deletes owned data. void TESSLINE::Clear() { if (loop == NULL) return; EDGEPT* this_edge = loop; do { EDGEPT* next_edge = this_edge->next; delete this_edge; this_edge = next_edge; } while (this_edge != loop); loop = NULL; } // Normalize in-place using the DENORM. void TESSLINE::Normalize(const DENORM& denorm) { EDGEPT* pt = loop; do { denorm.LocalNormTransform(pt->pos, &pt->pos); pt = pt->next; } while (pt != loop); SetupFromPos(); } // Rotates by the given rotation in place. void TESSLINE::Rotate(const FCOORD rot) { EDGEPT* pt = loop; do { int tmp = static_cast(floor(pt->pos.x * rot.x() - pt->pos.y * rot.y() + 0.5)); pt->pos.y = static_cast(floor(pt->pos.y * rot.x() + pt->pos.x * rot.y() + 0.5)); pt->pos.x = tmp; pt = pt->next; } while (pt != loop); SetupFromPos(); } // Moves by the given vec in place. void TESSLINE::Move(const ICOORD vec) { EDGEPT* pt = loop; do { pt->pos.x += vec.x(); pt->pos.y += vec.y(); pt = pt->next; } while (pt != loop); SetupFromPos(); } // Scales by the given factor in place. void TESSLINE::Scale(float factor) { EDGEPT* pt = loop; do { pt->pos.x = static_cast(floor(pt->pos.x * factor + 0.5)); pt->pos.y = static_cast(floor(pt->pos.y * factor + 0.5)); pt = pt->next; } while (pt != loop); SetupFromPos(); } // Sets up the start and vec members of the loop from the pos members. void TESSLINE::SetupFromPos() { EDGEPT* pt = loop; do { pt->vec.x = pt->next->pos.x - pt->pos.x; pt->vec.y = pt->next->pos.y - pt->pos.y; pt = pt->next; } while (pt != loop); start = pt->pos; ComputeBoundingBox(); } // Recomputes the bounding box from the points in the loop. void TESSLINE::ComputeBoundingBox() { int minx = MAX_INT32; int miny = MAX_INT32; int maxx = -MAX_INT32; int maxy = -MAX_INT32; // Find boundaries. start = loop->pos; EDGEPT* this_edge = loop; do { if (!this_edge->IsHidden() || !this_edge->prev->IsHidden()) { if (this_edge->pos.x < minx) minx = this_edge->pos.x; if (this_edge->pos.y < miny) miny = this_edge->pos.y; if (this_edge->pos.x > maxx) maxx = this_edge->pos.x; if (this_edge->pos.y > maxy) maxy = this_edge->pos.y; } this_edge = this_edge->next; } while (this_edge != loop); // Reset bounds. topleft.x = minx; topleft.y = maxy; botright.x = maxx; botright.y = miny; } // Computes the min and max cross product of the outline points with the // given vec and returns the results in min_xp and max_xp. Geometrically // this is the left and right edge of the outline perpendicular to the // given direction, but to get the distance units correct, you would // have to divide by the modulus of vec. void TESSLINE::MinMaxCrossProduct(const TPOINT vec, int* min_xp, int* max_xp) const { *min_xp = MAX_INT32; *max_xp = MIN_INT32; EDGEPT* this_edge = loop; do { if (!this_edge->IsHidden() || !this_edge->prev->IsHidden()) { int product = CROSS(this_edge->pos, vec); UpdateRange(product, min_xp, max_xp); } this_edge = this_edge->next; } while (this_edge != loop); } TBOX TESSLINE::bounding_box() const { return TBOX(topleft.x, botright.y, botright.x, topleft.y); } #ifndef GRAPHICS_DISABLED void TESSLINE::plot(ScrollView* window, ScrollView::Color color, ScrollView::Color child_color) { if (is_hole) window->Pen(child_color); else window->Pen(color); window->SetCursor(start.x, start.y); EDGEPT* pt = loop; do { bool prev_hidden = pt->IsHidden(); pt = pt->next; if (prev_hidden) window->SetCursor(pt->pos.x, pt->pos.y); else window->DrawTo(pt->pos.x, pt->pos.y); } while (pt != loop); } #endif // GRAPHICS_DISABLED // Returns the first non-hidden EDGEPT that has a different src_outline to // its predecessor, or, if all the same, the lowest indexed point. EDGEPT* TESSLINE::FindBestStartPt() const { EDGEPT* best_start = loop; int best_step = loop->start_step; // Iterate the polygon. EDGEPT* pt = loop; do { if (pt->IsHidden()) continue; if (pt->prev->IsHidden() || pt->prev->src_outline != pt->src_outline) return pt; // Qualifies as the best. if (pt->start_step < best_step) { best_step = pt->start_step; best_start = pt; } } while ((pt = pt->next) != loop); return best_start; } // Iterate the given list of outlines, converting to TESSLINE by polygonal // approximation and recursively any children, returning the current tail // of the resulting list of TESSLINEs. static TESSLINE** ApproximateOutlineList(bool allow_detailed_fx, C_OUTLINE_LIST* outlines, bool children, TESSLINE** tail) { C_OUTLINE_IT ol_it(outlines); for (ol_it.mark_cycle_pt(); !ol_it.cycled_list(); ol_it.forward()) { C_OUTLINE* outline = ol_it.data(); if (outline->pathlength() > 0) { TESSLINE* tessline = ApproximateOutline(allow_detailed_fx, outline); tessline->is_hole = children; *tail = tessline; tail = &tessline->next; } if (!outline->child()->empty()) { tail = ApproximateOutlineList(allow_detailed_fx, outline->child(), true, tail); } } return tail; } // Factory to build a TBLOB from a C_BLOB with polygonal approximation along // the way. If allow_detailed_fx is true, the EDGEPTs in the returned TBLOB // contain pointers to the input C_OUTLINEs that enable higher-resolution // feature extraction that does not use the polygonal approximation. TBLOB* TBLOB::PolygonalCopy(bool allow_detailed_fx, C_BLOB* src) { TBLOB* tblob = new TBLOB; ApproximateOutlineList(allow_detailed_fx, src->out_list(), false, &tblob->outlines); return tblob; } // Factory builds a blob with no outlines, but copies the other member data. TBLOB* TBLOB::ShallowCopy(const TBLOB& src) { TBLOB* blob = new TBLOB; blob->denorm_ = src.denorm_; return blob; } // Normalizes the blob for classification only if needed. // (Normally this means a non-zero classify rotation.) // If no Normalization is needed, then NULL is returned, and the input blob // can be used directly. Otherwise a new TBLOB is returned which must be // deleted after use. TBLOB* TBLOB::ClassifyNormalizeIfNeeded() const { TBLOB* rotated_blob = NULL; // If necessary, copy the blob and rotate it. The rotation is always // +/- 90 degrees, as 180 was already taken care of. if (denorm_.block() != NULL && denorm_.block()->classify_rotation().y() != 0.0) { TBOX box = bounding_box(); int x_middle = (box.left() + box.right()) / 2; int y_middle = (box.top() + box.bottom()) / 2; rotated_blob = new TBLOB(*this); const FCOORD& rotation = denorm_.block()->classify_rotation(); // Move the rotated blob back to the same y-position so that we // can still distinguish similar glyphs with differeny y-position. float target_y = kBlnBaselineOffset + (rotation.y() > 0 ? x_middle - box.left() : box.right() - x_middle); rotated_blob->Normalize(NULL, &rotation, &denorm_, x_middle, y_middle, 1.0f, 1.0f, 0.0f, target_y, denorm_.inverse(), denorm_.pix()); } return rotated_blob; } // Copies the data and the outline, but leaves next untouched. void TBLOB::CopyFrom(const TBLOB& src) { Clear(); TESSLINE* prev_outline = NULL; for (TESSLINE* srcline = src.outlines; srcline != NULL; srcline = srcline->next) { TESSLINE* new_outline = new TESSLINE(*srcline); if (outlines == NULL) outlines = new_outline; else prev_outline->next = new_outline; prev_outline = new_outline; } denorm_ = src.denorm_; } // Deletes owned data. void TBLOB::Clear() { for (TESSLINE* next_outline = NULL; outlines != NULL; outlines = next_outline) { next_outline = outlines->next; delete outlines; } } // Sets up the built-in DENORM and normalizes the blob in-place. // For parameters see DENORM::SetupNormalization, plus the inverse flag for // this blob and the Pix for the full image. void TBLOB::Normalize(const BLOCK* block, const FCOORD* rotation, const DENORM* predecessor, float x_origin, float y_origin, float x_scale, float y_scale, float final_xshift, float final_yshift, bool inverse, Pix* pix) { denorm_.SetupNormalization(block, rotation, predecessor, x_origin, y_origin, x_scale, y_scale, final_xshift, final_yshift); denorm_.set_inverse(inverse); denorm_.set_pix(pix); // TODO(rays) outline->Normalize is more accurate, but breaks tests due // the changes it makes. Reinstate this code with a retraining. // The reason this change is troublesome is that it normalizes for the // baseline value computed independently at each x-coord. If the baseline // is not horizontal, this introduces shear into the normalized blob, which // is useful on the rare occasions that the baseline is really curved, but // the baselines need to be stabilized the rest of the time. #if 0 for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) { outline->Normalize(denorm_); } #else denorm_.LocalNormBlob(this); #endif } // Rotates by the given rotation in place. void TBLOB::Rotate(const FCOORD rotation) { for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) { outline->Rotate(rotation); } } // Moves by the given vec in place. void TBLOB::Move(const ICOORD vec) { for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) { outline->Move(vec); } } // Scales by the given factor in place. void TBLOB::Scale(float factor) { for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) { outline->Scale(factor); } } // Recomputes the bounding boxes of the outlines. void TBLOB::ComputeBoundingBoxes() { for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) { outline->ComputeBoundingBox(); } } // Returns the number of outlines. int TBLOB::NumOutlines() const { int result = 0; for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) ++result; return result; } /********************************************************************** * TBLOB::bounding_box() * * Compute the bounding_box of a compound blob, defined to be the * bounding box of the union of all top-level outlines in the blob. **********************************************************************/ TBOX TBLOB::bounding_box() const { if (outlines == NULL) return TBOX(0, 0, 0, 0); TESSLINE *outline = outlines; TBOX box = outline->bounding_box(); for (outline = outline->next; outline != NULL; outline = outline->next) { box += outline->bounding_box(); } return box; } // Finds and deletes any duplicate outlines in this blob, without deleting // their EDGEPTs. void TBLOB::EliminateDuplicateOutlines() { for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) { TESSLINE* last_outline = outline; for (TESSLINE* other_outline = outline->next; other_outline != NULL; last_outline = other_outline, other_outline = other_outline->next) { if (outline->SameBox(*other_outline)) { last_outline->next = other_outline->next; // This doesn't leak - the outlines share the EDGEPTs. other_outline->loop = NULL; delete other_outline; other_outline = last_outline; // If it is part of a cut, then it can't be a hole any more. outline->is_hole = false; } } } } // Swaps the outlines of *this and next if needed to keep the centers in // increasing x. void TBLOB::CorrectBlobOrder(TBLOB* next) { TBOX box = bounding_box(); TBOX next_box = next->bounding_box(); if (box.x_middle() > next_box.x_middle()) { Swap(&outlines, &next->outlines); } } #ifndef GRAPHICS_DISABLED void TBLOB::plot(ScrollView* window, ScrollView::Color color, ScrollView::Color child_color) { for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) outline->plot(window, color, child_color); } #endif // GRAPHICS_DISABLED // Computes the center of mass and second moments for the old baseline and // 2nd moment normalizations. Returns the outline length. // The input denorm should be the normalizations that have been applied from // the image to the current state of this TBLOB. int TBLOB::ComputeMoments(FCOORD* center, FCOORD* second_moments) const { // Compute 1st and 2nd moments of the original outline. LLSQ accumulator; TBOX box = bounding_box(); // Iterate the outlines, accumulating edges relative the box.botleft(). CollectEdges(box, NULL, &accumulator, NULL, NULL); *center = accumulator.mean_point() + box.botleft(); // The 2nd moments are just the standard deviation of the point positions. double x2nd = sqrt(accumulator.x_variance()); double y2nd = sqrt(accumulator.y_variance()); if (x2nd < 1.0) x2nd = 1.0; if (y2nd < 1.0) y2nd = 1.0; second_moments->set_x(x2nd); second_moments->set_y(y2nd); return accumulator.count(); } // Computes the precise bounding box of the coords that are generated by // GetEdgeCoords. This may be different from the bounding box of the polygon. void TBLOB::GetPreciseBoundingBox(TBOX* precise_box) const { TBOX box = bounding_box(); *precise_box = TBOX(); CollectEdges(box, precise_box, NULL, NULL, NULL); precise_box->move(box.botleft()); } // Adds edges to the given vectors. // For all the edge steps in all the outlines, or polygonal approximation // where there are no edge steps, collects the steps into x_coords/y_coords. // x_coords is a collection of the x-coords of vertical edges for each // y-coord starting at box.bottom(). // y_coords is a collection of the y-coords of horizontal edges for each // x-coord starting at box.left(). // Eg x_coords[0] is a collection of the x-coords of edges at y=bottom. // Eg x_coords[1] is a collection of the x-coords of edges at y=bottom + 1. void TBLOB::GetEdgeCoords(const TBOX& box, GenericVector >* x_coords, GenericVector >* y_coords) const { GenericVector empty; x_coords->init_to_size(box.height(), empty); y_coords->init_to_size(box.width(), empty); CollectEdges(box, NULL, NULL, x_coords, y_coords); // Sort the output vectors. for (int i = 0; i < x_coords->size(); ++i) (*x_coords)[i].sort(); for (int i = 0; i < y_coords->size(); ++i) (*y_coords)[i].sort(); } // Accumulates the segment between pt1 and pt2 in the LLSQ, quantizing over // the integer coordinate grid to properly weight long vectors. static void SegmentLLSQ(const FCOORD& pt1, const FCOORD& pt2, LLSQ* accumulator) { FCOORD step(pt2); step -= pt1; int xstart = IntCastRounded(MIN(pt1.x(), pt2.x())); int xend = IntCastRounded(MAX(pt1.x(), pt2.x())); int ystart = IntCastRounded(MIN(pt1.y(), pt2.y())); int yend = IntCastRounded(MAX(pt1.y(), pt2.y())); if (xstart == xend && ystart == yend) return; // Nothing to do. double weight = step.length() / (xend - xstart + yend - ystart); // Compute and save the y-position at the middle of each x-step. for (int x = xstart; x < xend; ++x) { double y = pt1.y() + step.y() * (x + 0.5 - pt1.x()) / step.x(); accumulator->add(x + 0.5, y, weight); } // Compute and save the x-position at the middle of each y-step. for (int y = ystart; y < yend; ++y) { double x = pt1.x() + step.x() * (y + 0.5 - pt1.y()) / step.y(); accumulator->add(x, y + 0.5, weight); } } // Adds any edges from a single segment of outline between pt1 and pt2 to // the x_coords, y_coords vectors. pt1 and pt2 should be relative to the // bottom-left of the bounding box, hence indices to x_coords, y_coords // are clipped to ([0,x_limit], [0,y_limit]). // See GetEdgeCoords above for a description of x_coords, y_coords. static void SegmentCoords(const FCOORD& pt1, const FCOORD& pt2, int x_limit, int y_limit, GenericVector >* x_coords, GenericVector >* y_coords) { FCOORD step(pt2); step -= pt1; int start = ClipToRange(IntCastRounded(MIN(pt1.x(), pt2.x())), 0, x_limit); int end = ClipToRange(IntCastRounded(MAX(pt1.x(), pt2.x())), 0, x_limit); for (int x = start; x < end; ++x) { int y = IntCastRounded(pt1.y() + step.y() * (x + 0.5 - pt1.x()) / step.x()); (*y_coords)[x].push_back(y); } start = ClipToRange(IntCastRounded(MIN(pt1.y(), pt2.y())), 0, y_limit); end = ClipToRange(IntCastRounded(MAX(pt1.y(), pt2.y())), 0, y_limit); for (int y = start; y < end; ++y) { int x = IntCastRounded(pt1.x() + step.x() * (y + 0.5 - pt1.y()) / step.y()); (*x_coords)[y].push_back(x); } } // Adds any edges from a single segment of outline between pt1 and pt2 to // the bbox such that it guarantees to contain anything produced by // SegmentCoords. static void SegmentBBox(const FCOORD& pt1, const FCOORD& pt2, TBOX* bbox) { FCOORD step(pt2); step -= pt1; int x1 = IntCastRounded(MIN(pt1.x(), pt2.x())); int x2 = IntCastRounded(MAX(pt1.x(), pt2.x())); if (x2 > x1) { int y1 = IntCastRounded(pt1.y() + step.y() * (x1 + 0.5 - pt1.x()) / step.x()); int y2 = IntCastRounded(pt1.y() + step.y() * (x2 - 0.5 - pt1.x()) / step.x()); TBOX point(x1, MIN(y1, y2), x2, MAX(y1, y2)); *bbox += point; } int y1 = IntCastRounded(MIN(pt1.y(), pt2.y())); int y2 = IntCastRounded(MAX(pt1.y(), pt2.y())); if (y2 > y1) { int x1 = IntCastRounded(pt1.x() + step.x() * (y1 + 0.5 - pt1.y()) / step.y()); int x2 = IntCastRounded(pt1.x() + step.x() * (y2 - 0.5 - pt1.y()) / step.y()); TBOX point(MIN(x1, x2), y1, MAX(x1, x2), y2); *bbox += point; } } // Collects edges into the given bounding box, LLSQ accumulator and/or x_coords, // y_coords vectors. // For a description of x_coords/y_coords, see GetEdgeCoords above. // Startpt to lastpt, inclusive, MUST have the same src_outline member, // which may be NULL. The vector from lastpt to its next is included in // the accumulation. Hidden edges should be excluded by the caller. // The input denorm should be the normalizations that have been applied from // the image to the current state of the TBLOB from which startpt, lastpt come. // box is the bounding box of the blob from which the EDGEPTs are taken and // indices into x_coords, y_coords are offset by box.botleft(). static void CollectEdgesOfRun(const EDGEPT* startpt, const EDGEPT* lastpt, const DENORM& denorm, const TBOX& box, TBOX* bounding_box, LLSQ* accumulator, GenericVector > *x_coords, GenericVector > *y_coords) { const C_OUTLINE* outline = startpt->src_outline; int x_limit = box.width() - 1; int y_limit = box.height() - 1; if (outline != NULL) { // Use higher-resolution edge points stored on the outline. // The outline coordinates may not match the binary image because of the // rotation for vertical text lines, but the root_denorm IS the matching // start of the DENORM chain. const DENORM* root_denorm = denorm.RootDenorm(); int step_length = outline->pathlength(); int start_index = startpt->start_step; // Note that if this run straddles the wrap-around point of the outline, // that lastpt->start_step may have a lower index than startpt->start_step, // and we want to use an end_index that allows us to use a positive // increment, so we add step_length if necessary, but that may be beyond the // bounds of the outline steps/ due to wrap-around, so we use % step_length // everywhere, except for start_index. int end_index = lastpt->start_step + lastpt->step_count; if (end_index <= start_index) end_index += step_length; // pos is the integer coordinates of the binary image steps. ICOORD pos = outline->position_at_index(start_index); FCOORD origin(box.left(), box.bottom()); // f_pos is a floating-point version of pos that offers improved edge // positioning using greyscale information or smoothing of edge steps. FCOORD f_pos = outline->sub_pixel_pos_at_index(pos, start_index); // pos_normed is f_pos after the appropriate normalization, and relative // to origin. // prev_normed is the previous value of pos_normed. FCOORD prev_normed; denorm.NormTransform(root_denorm, f_pos, &prev_normed); prev_normed -= origin; for (int index = start_index; index < end_index; ++index) { ICOORD step = outline->step(index % step_length); // Only use the point if its edge strength is positive. This excludes // points that don't provide useful information, eg // ___________ // |___________ // The vertical step provides only noisy, damaging information, as even // with a greyscale image, the positioning of the edge there may be a // fictitious extrapolation, so previous processing has eliminated it. if (outline->edge_strength_at_index(index % step_length) > 0) { FCOORD f_pos = outline->sub_pixel_pos_at_index(pos, index % step_length); FCOORD pos_normed; denorm.NormTransform(root_denorm, f_pos, &pos_normed); pos_normed -= origin; // Accumulate the information that is selected by the caller. if (bounding_box != NULL) { SegmentBBox(pos_normed, prev_normed, bounding_box); } if (accumulator != NULL) { SegmentLLSQ(pos_normed, prev_normed, accumulator); } if (x_coords != NULL && y_coords != NULL) { SegmentCoords(pos_normed, prev_normed, x_limit, y_limit, x_coords, y_coords); } prev_normed = pos_normed; } pos += step; } } else { // There is no outline, so we are forced to use the polygonal approximation. const EDGEPT* endpt = lastpt->next; const EDGEPT* pt = startpt; do { FCOORD next_pos(pt->next->pos.x - box.left(), pt->next->pos.y - box.bottom()); FCOORD pos(pt->pos.x - box.left(), pt->pos.y - box.bottom()); if (bounding_box != NULL) { SegmentBBox(next_pos, pos, bounding_box); } if (accumulator != NULL) { SegmentLLSQ(next_pos, pos, accumulator); } if (x_coords != NULL && y_coords != NULL) { SegmentCoords(next_pos, pos, x_limit, y_limit, x_coords, y_coords); } } while ((pt = pt->next) != endpt); } } // For all the edge steps in all the outlines, or polygonal approximation // where there are no edge steps, collects the steps into the bounding_box, // llsq and/or the x_coords/y_coords. Both are used in different kinds of // normalization. // For a description of x_coords, y_coords, see GetEdgeCoords above. void TBLOB::CollectEdges(const TBOX& box, TBOX* bounding_box, LLSQ* llsq, GenericVector >* x_coords, GenericVector >* y_coords) const { // Iterate the outlines. for (const TESSLINE* ol = outlines; ol != NULL; ol = ol->next) { // Iterate the polygon. EDGEPT* loop_pt = ol->FindBestStartPt(); EDGEPT* pt = loop_pt; if (pt == NULL) continue; do { if (pt->IsHidden()) continue; // Find a run of equal src_outline. EDGEPT* last_pt = pt; do { last_pt = last_pt->next; } while (last_pt != loop_pt && !last_pt->IsHidden() && last_pt->src_outline == pt->src_outline); last_pt = last_pt->prev; CollectEdgesOfRun(pt, last_pt, denorm_, box, bounding_box, llsq, x_coords, y_coords); pt = last_pt; } while ((pt = pt->next) != loop_pt); } } // Factory to build a TWERD from a (C_BLOB) WERD, with polygonal // approximation along the way. TWERD* TWERD::PolygonalCopy(bool allow_detailed_fx, WERD* src) { TWERD* tessword = new TWERD; tessword->latin_script = src->flag(W_SCRIPT_IS_LATIN); C_BLOB_IT b_it(src->cblob_list()); for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { C_BLOB* blob = b_it.data(); TBLOB* tblob = TBLOB::PolygonalCopy(allow_detailed_fx, blob); tessword->blobs.push_back(tblob); } return tessword; } // Baseline normalizes the blobs in-place, recording the normalization in the // DENORMs in the blobs. void TWERD::BLNormalize(const BLOCK* block, const ROW* row, Pix* pix, bool inverse, float x_height, float baseline_shift, bool numeric_mode, tesseract::OcrEngineMode hint, const TBOX* norm_box, DENORM* word_denorm) { TBOX word_box = bounding_box(); if (norm_box != NULL) word_box = *norm_box; float word_middle = (word_box.left() + word_box.right()) / 2.0f; float input_y_offset = 0.0f; float final_y_offset = static_cast(kBlnBaselineOffset); float scale = kBlnXHeight / x_height; if (hint == tesseract::OEM_CUBE_ONLY || row == NULL) { word_middle = word_box.left(); input_y_offset = word_box.bottom(); final_y_offset = 0.0f; if (hint == tesseract::OEM_CUBE_ONLY) scale = 1.0f; } else { input_y_offset = row->base_line(word_middle) + baseline_shift; } for (int b = 0; b < blobs.size(); ++b) { TBLOB* blob = blobs[b]; TBOX blob_box = blob->bounding_box(); float mid_x = (blob_box.left() + blob_box.right()) / 2.0f; float baseline = input_y_offset; float blob_scale = scale; if (numeric_mode) { baseline = blob_box.bottom(); blob_scale = ClipToRange(kBlnXHeight * 4.0f / (3 * blob_box.height()), scale, scale * 1.5f); } else if (row != NULL && hint != tesseract::OEM_CUBE_ONLY) { baseline = row->base_line(mid_x) + baseline_shift; } // 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. // 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. blob->Normalize(block, NULL, NULL, word_middle, baseline, blob_scale, blob_scale, 0.0f, final_y_offset, inverse, pix); } if (word_denorm != NULL) { word_denorm->SetupNormalization(block, NULL, NULL, word_middle, input_y_offset, scale, scale, 0.0f, final_y_offset); word_denorm->set_inverse(inverse); word_denorm->set_pix(pix); } } // Copies the data and the blobs, but leaves next untouched. void TWERD::CopyFrom(const TWERD& src) { Clear(); latin_script = src.latin_script; for (int b = 0; b < src.blobs.size(); ++b) { TBLOB* new_blob = new TBLOB(*src.blobs[b]); blobs.push_back(new_blob); } } // Deletes owned data. void TWERD::Clear() { blobs.delete_data_pointers(); blobs.clear(); } // Recomputes the bounding boxes of the blobs. void TWERD::ComputeBoundingBoxes() { for (int b = 0; b < blobs.size(); ++b) { blobs[b]->ComputeBoundingBoxes(); } } TBOX TWERD::bounding_box() const { TBOX result; for (int b = 0; b < blobs.size(); ++b) { TBOX box = blobs[b]->bounding_box(); result += box; } return result; } // Merges the blobs from start to end, not including end, and deletes // the blobs between start and end. void TWERD::MergeBlobs(int start, int end) { if (start >= blobs.size() - 1) return; // Nothing to do. TESSLINE* outline = blobs[start]->outlines; for (int i = start + 1; i < end && i < blobs.size(); ++i) { TBLOB* next_blob = blobs[i]; // Take the outlines from the next blob. if (outline == NULL) { blobs[start]->outlines = next_blob->outlines; outline = blobs[start]->outlines; } else { while (outline->next != NULL) outline = outline->next; outline->next = next_blob->outlines; next_blob->outlines = NULL; } // Delete the next blob and move on. delete next_blob; blobs[i] = NULL; } // Remove dead blobs from the vector. for (int i = start + 1; i < end && start + 1 < blobs.size(); ++i) { blobs.remove(start + 1); } } #ifndef GRAPHICS_DISABLED void TWERD::plot(ScrollView* window) { ScrollView::Color color = WERD::NextColor(ScrollView::BLACK); for (int b = 0; b < blobs.size(); ++b) { blobs[b]->plot(window, color, ScrollView::BROWN); color = WERD::NextColor(color); } } #endif // GRAPHICS_DISABLED /********************************************************************** * divisible_blob * * Returns true if the blob contains multiple outlines than can be * separated using divide_blobs. Sets the location to be used in the * call to divide_blobs. **********************************************************************/ bool divisible_blob(TBLOB *blob, bool italic_blob, TPOINT* location) { if (blob->outlines == NULL || blob->outlines->next == NULL) return false; // Need at least 2 outlines for it to be possible. int max_gap = 0; TPOINT vertical = italic_blob ? kDivisibleVerticalItalic : kDivisibleVerticalUpright; for (TESSLINE* outline1 = blob->outlines; outline1 != NULL; outline1 = outline1->next) { if (outline1->is_hole) continue; // Holes do not count as separable. TPOINT mid_pt1( static_cast((outline1->topleft.x + outline1->botright.x) / 2), static_cast((outline1->topleft.y + outline1->botright.y) / 2)); int mid_prod1 = CROSS(mid_pt1, vertical); int min_prod1, max_prod1; outline1->MinMaxCrossProduct(vertical, &min_prod1, &max_prod1); for (TESSLINE* outline2 = outline1->next; outline2 != NULL; outline2 = outline2->next) { if (outline2->is_hole) continue; // Holes do not count as separable. TPOINT mid_pt2( static_cast((outline2->topleft.x + outline2->botright.x) / 2), static_cast((outline2->topleft.y + outline2->botright.y) / 2)); int mid_prod2 = CROSS(mid_pt2, vertical); int min_prod2, max_prod2; outline2->MinMaxCrossProduct(vertical, &min_prod2, &max_prod2); int mid_gap = abs(mid_prod2 - mid_prod1); int overlap = MIN(max_prod1, max_prod2) - MAX(min_prod1, min_prod2); if (mid_gap - overlap / 4 > max_gap) { max_gap = mid_gap - overlap / 4; *location = mid_pt1; *location += mid_pt2; *location /= 2; } } } // Use the y component of the vertical vector as an approximation to its // length. return max_gap > vertical.y; } /********************************************************************** * divide_blobs * * Create two blobs by grouping the outlines in the appropriate blob. * The outlines that are beyond the location point are moved to the * other blob. The ones whose x location is less than that point are * retained in the original blob. **********************************************************************/ void divide_blobs(TBLOB *blob, TBLOB *other_blob, bool italic_blob, const TPOINT& location) { TPOINT vertical = italic_blob ? kDivisibleVerticalItalic : kDivisibleVerticalUpright; TESSLINE *outline1 = NULL; TESSLINE *outline2 = NULL; TESSLINE *outline = blob->outlines; blob->outlines = NULL; int location_prod = CROSS(location, vertical); while (outline != NULL) { TPOINT mid_pt( static_cast((outline->topleft.x + outline->botright.x) / 2), static_cast((outline->topleft.y + outline->botright.y) / 2)); int mid_prod = CROSS(mid_pt, vertical); if (mid_prod < location_prod) { // Outline is in left blob. if (outline1) outline1->next = outline; else blob->outlines = outline; outline1 = outline; } else { // Outline is in right blob. if (outline2) outline2->next = outline; else other_blob->outlines = outline; outline2 = outline; } outline = outline->next; } if (outline1) outline1->next = NULL; if (outline2) outline2->next = NULL; } tesseract-3.04.01/ccstruct/blobs.h000066400000000000000000000412111266071204500167620ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: blobs.h (Formerly blobs.h) * Description: Blob definition * Author: Mark Seaman, OCR Technology * Created: Fri Oct 27 15:39:52 1989 * Modified: Thu Mar 28 15:33:38 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 BLOBS_H #define BLOBS_H /*---------------------------------------------------------------------- I n c l u d e s ----------------------------------------------------------------------*/ #include "clst.h" #include "normalis.h" #include "publictypes.h" #include "rect.h" #include "vecfuncs.h" class BLOCK; class C_BLOB; class C_OUTLINE; class LLSQ; class ROW; class WERD; /*---------------------------------------------------------------------- T y p e s ----------------------------------------------------------------------*/ #define EDGEPTFLAGS 4 /*concavity,length etc. */ struct TPOINT { TPOINT(): x(0), y(0) {} TPOINT(inT16 vx, inT16 vy) : x(vx), y(vy) {} TPOINT(const ICOORD &ic) : x(ic.x()), y(ic.y()) {} void operator+=(const TPOINT& other) { x += other.x; y += other.y; } void operator/=(int divisor) { x /= divisor; y /= divisor; } bool operator==(const TPOINT& other) const { return x == other.x && y == other.y; } // Returns true when the two line segments cross each other. // (Moved from outlines.cpp). static bool IsCrossed(const TPOINT& a0, const TPOINT& a1, const TPOINT& b0, const TPOINT& b1); inT16 x; // absolute x coord. inT16 y; // absolute y coord. }; typedef TPOINT VECTOR; // structure for coordinates. struct EDGEPT { EDGEPT() : next(NULL), prev(NULL), src_outline(NULL), start_step(0), step_count(0) { memset(flags, 0, EDGEPTFLAGS * sizeof(flags[0])); } EDGEPT(const EDGEPT& src) : next(NULL), prev(NULL) { CopyFrom(src); } EDGEPT& operator=(const EDGEPT& src) { CopyFrom(src); return *this; } // Copies the data elements, but leaves the pointers untouched. void CopyFrom(const EDGEPT& src) { pos = src.pos; vec = src.vec; memcpy(flags, src.flags, EDGEPTFLAGS * sizeof(flags[0])); src_outline = src.src_outline; start_step = src.start_step; step_count = src.step_count; } // Returns the squared distance between the points, with the x-component // weighted by x_factor. int WeightedDistance(const EDGEPT& other, int x_factor) const { int x_dist = pos.x - other.pos.x; int y_dist = pos.y - other.pos.y; return x_dist * x_dist * x_factor + y_dist * y_dist; } // Returns true if the positions are equal. bool EqualPos(const EDGEPT& other) const { return pos == other.pos; } // Returns the bounding box of the outline segment from *this to *end. // Ignores hidden edge flags. TBOX SegmentBox(const EDGEPT* end) const { TBOX box(pos.x, pos.y, pos.x, pos.y); const EDGEPT* pt = this; do { pt = pt->next; if (pt->pos.x < box.left()) box.set_left(pt->pos.x); if (pt->pos.x > box.right()) box.set_right(pt->pos.x); if (pt->pos.y < box.bottom()) box.set_bottom(pt->pos.y); if (pt->pos.y > box.top()) box.set_top(pt->pos.y); } while (pt != end && pt != this); return box; } // Returns the area of the outline segment from *this to *end. // Ignores hidden edge flags. int SegmentArea(const EDGEPT* end) const { int area = 0; const EDGEPT* pt = this->next; do { TPOINT origin_vec(pt->pos.x - pos.x, pt->pos.y - pos.y); area += CROSS(origin_vec, pt->vec); pt = pt->next; } while (pt != end && pt != this); return area; } // Returns true if the number of points in the outline segment from *this to // *end is less that min_points and false if we get back to *this first. // Ignores hidden edge flags. bool ShortNonCircularSegment(int min_points, const EDGEPT* end) const { int count = 0; const EDGEPT* pt = this; do { if (pt == end) return true; pt = pt->next; ++count; } while (pt != this && count <= min_points); return false; } // Accessors to hide or reveal a cut edge from feature extractors. void Hide() { flags[0] = true; } void Reveal() { flags[0] = false; } bool IsHidden() const { return flags[0] != 0; } void MarkChop() { flags[2] = true; } bool IsChopPt() const { return flags[2] != 0; } TPOINT pos; // position VECTOR vec; // vector to next point // TODO(rays) Remove flags and replace with // is_hidden, runlength, dir, and fixed. The only use // of the flags other than is_hidden is in polyaprx.cpp. char flags[EDGEPTFLAGS]; // concavity, length etc EDGEPT* next; // anticlockwise element EDGEPT* prev; // clockwise element C_OUTLINE* src_outline; // Outline it came from. // The following fields are not used if src_outline is NULL. int start_step; // Location of pos in src_outline. int step_count; // Number of steps used (may wrap around). }; // For use in chop and findseam to keep a list of which EDGEPTs were inserted. CLISTIZEH(EDGEPT); struct TESSLINE { TESSLINE() : is_hole(false), loop(NULL), next(NULL) {} TESSLINE(const TESSLINE& src) : loop(NULL), next(NULL) { CopyFrom(src); } ~TESSLINE() { Clear(); } TESSLINE& operator=(const TESSLINE& src) { CopyFrom(src); return *this; } // Consume the circular list of EDGEPTs to make a TESSLINE. static TESSLINE* BuildFromOutlineList(EDGEPT* outline); // Copies the data and the outline, but leaves next untouched. void CopyFrom(const TESSLINE& src); // Deletes owned data. void Clear(); // Normalize in-place using the DENORM. void Normalize(const DENORM& denorm); // Rotates by the given rotation in place. void Rotate(const FCOORD rotation); // Moves by the given vec in place. void Move(const ICOORD vec); // Scales by the given factor in place. void Scale(float factor); // Sets up the start and vec members of the loop from the pos members. void SetupFromPos(); // Recomputes the bounding box from the points in the loop. void ComputeBoundingBox(); // Computes the min and max cross product of the outline points with the // given vec and returns the results in min_xp and max_xp. Geometrically // this is the left and right edge of the outline perpendicular to the // given direction, but to get the distance units correct, you would // have to divide by the modulus of vec. void MinMaxCrossProduct(const TPOINT vec, int* min_xp, int* max_xp) const; TBOX bounding_box() const; // Returns true if *this and other have equal bounding boxes. bool SameBox(const TESSLINE& other) const { return topleft == other.topleft && botright == other.botright; } // Returns true if the given line segment crosses any outline of this blob. bool SegmentCrosses(const TPOINT& pt1, const TPOINT& pt2) const { if (Contains(pt1) && Contains(pt2)) { EDGEPT* pt = loop; do { if (TPOINT::IsCrossed(pt1, pt2, pt->pos, pt->next->pos)) return true; pt = pt->next; } while (pt != loop); } return false; } // Returns true if the point is contained within the outline box. bool Contains(const TPOINT& pt) const { return topleft.x <= pt.x && pt.x <= botright.x && botright.y <= pt.y && pt.y <= topleft.y; } #ifndef GRAPHICS_DISABLED void plot(ScrollView* window, ScrollView::Color color, ScrollView::Color child_color); #endif // GRAPHICS_DISABLED // Returns the first outline point that has a different src_outline to its // predecessor, or, if all the same, the lowest indexed point. EDGEPT* FindBestStartPt() const; int BBArea() const { return (botright.x - topleft.x) * (topleft.y - botright.y); } TPOINT topleft; // Top left of loop. TPOINT botright; // Bottom right of loop. TPOINT start; // Start of loop. bool is_hole; // True if this is a hole/child outline. EDGEPT *loop; // Edgeloop. TESSLINE *next; // Next outline in blob. }; // Outline structure. struct TBLOB { TBLOB() : outlines(NULL) {} TBLOB(const TBLOB& src) : outlines(NULL) { CopyFrom(src); } ~TBLOB() { Clear(); } TBLOB& operator=(const TBLOB& src) { CopyFrom(src); return *this; } // Factory to build a TBLOB from a C_BLOB with polygonal approximation along // the way. If allow_detailed_fx is true, the EDGEPTs in the returned TBLOB // contain pointers to the input C_OUTLINEs that enable higher-resolution // feature extraction that does not use the polygonal approximation. static TBLOB* PolygonalCopy(bool allow_detailed_fx, C_BLOB* src); // Factory builds a blob with no outlines, but copies the other member data. static TBLOB* ShallowCopy(const TBLOB& src); // Normalizes the blob for classification only if needed. // (Normally this means a non-zero classify rotation.) // If no Normalization is needed, then NULL is returned, and the input blob // can be used directly. Otherwise a new TBLOB is returned which must be // deleted after use. TBLOB* ClassifyNormalizeIfNeeded() const; // Copies the data and the outlines, but leaves next untouched. void CopyFrom(const TBLOB& src); // Deletes owned data. void Clear(); // Sets up the built-in DENORM and normalizes the blob in-place. // For parameters see DENORM::SetupNormalization, plus the inverse flag for // this blob and the Pix for the full image. void Normalize(const BLOCK* block, const FCOORD* rotation, const DENORM* predecessor, float x_origin, float y_origin, float x_scale, float y_scale, float final_xshift, float final_yshift, bool inverse, Pix* pix); // Rotates by the given rotation in place. void Rotate(const FCOORD rotation); // Moves by the given vec in place. void Move(const ICOORD vec); // Scales by the given factor in place. void Scale(float factor); // Recomputes the bounding boxes of the outlines. void ComputeBoundingBoxes(); // Returns the number of outlines. int NumOutlines() const; TBOX bounding_box() const; // Returns true if the given line segment crosses any outline of this blob. bool SegmentCrossesOutline(const TPOINT& pt1, const TPOINT& pt2) const { for (const TESSLINE* outline = outlines; outline != NULL; outline = outline->next) { if (outline->SegmentCrosses(pt1, pt2)) return true; } return false; } // Returns true if the point is contained within any of the outline boxes. bool Contains(const TPOINT& pt) const { for (const TESSLINE* outline = outlines; outline != NULL; outline = outline->next) { if (outline->Contains(pt)) return true; } return false; } // Finds and deletes any duplicate outlines in this blob, without deleting // their EDGEPTs. void EliminateDuplicateOutlines(); // Swaps the outlines of *this and next if needed to keep the centers in // increasing x. void CorrectBlobOrder(TBLOB* next); const DENORM& denorm() const { return denorm_; } #ifndef GRAPHICS_DISABLED void plot(ScrollView* window, ScrollView::Color color, ScrollView::Color child_color); #endif // GRAPHICS_DISABLED int BBArea() const { int total_area = 0; for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) total_area += outline->BBArea(); return total_area; } // Computes the center of mass and second moments for the old baseline and // 2nd moment normalizations. Returns the outline length. // The input denorm should be the normalizations that have been applied from // the image to the current state of this TBLOB. int ComputeMoments(FCOORD* center, FCOORD* second_moments) const; // Computes the precise bounding box of the coords that are generated by // GetEdgeCoords. This may be different from the bounding box of the polygon. void GetPreciseBoundingBox(TBOX* precise_box) const; // Adds edges to the given vectors. // For all the edge steps in all the outlines, or polygonal approximation // where there are no edge steps, collects the steps into x_coords/y_coords. // x_coords is a collection of the x-coords of vertical edges for each // y-coord starting at box.bottom(). // y_coords is a collection of the y-coords of horizontal edges for each // x-coord starting at box.left(). // Eg x_coords[0] is a collection of the x-coords of edges at y=bottom. // Eg x_coords[1] is a collection of the x-coords of edges at y=bottom + 1. void GetEdgeCoords(const TBOX& box, GenericVector >* x_coords, GenericVector >* y_coords) const; TESSLINE *outlines; // List of outlines in blob. private: // TODO(rays) Someday the data members will be private too. // For all the edge steps in all the outlines, or polygonal approximation // where there are no edge steps, collects the steps into the bounding_box, // llsq and/or the x_coords/y_coords. Both are used in different kinds of // normalization. // For a description of x_coords, y_coords, see GetEdgeCoords above. void CollectEdges(const TBOX& box, TBOX* bounding_box, LLSQ* llsq, GenericVector >* x_coords, GenericVector >* y_coords) const; private: // DENORM indicating the transformations that this blob has undergone so far. DENORM denorm_; }; // Blob structure. struct TWERD { TWERD() : latin_script(false) {} TWERD(const TWERD& src) { CopyFrom(src); } ~TWERD() { Clear(); } TWERD& operator=(const TWERD& src) { CopyFrom(src); return *this; } // Factory to build a TWERD from a (C_BLOB) WERD, with polygonal // approximation along the way. static TWERD* PolygonalCopy(bool allow_detailed_fx, WERD* src); // Baseline normalizes the blobs in-place, recording the normalization in the // DENORMs in the blobs. void BLNormalize(const BLOCK* block, const ROW* row, Pix* pix, bool inverse, float x_height, float baseline_shift, bool numeric_mode, tesseract::OcrEngineMode hint, const TBOX* norm_box, DENORM* word_denorm); // Copies the data and the blobs, but leaves next untouched. void CopyFrom(const TWERD& src); // Deletes owned data. void Clear(); // Recomputes the bounding boxes of the blobs. void ComputeBoundingBoxes(); // Returns the number of blobs in the word. int NumBlobs() const { return blobs.size(); } TBOX bounding_box() const; // Merges the blobs from start to end, not including end, and deletes // the blobs between start and end. void MergeBlobs(int start, int end); void plot(ScrollView* window); GenericVector blobs; // Blobs in word. bool latin_script; // This word is in a latin-based script. }; /*---------------------------------------------------------------------- M a c r o s ----------------------------------------------------------------------*/ /********************************************************************** * free_widths * * Free the memory taken up by a width array. **********************************************************************/ #define free_widths(w) \ if (w) memfree (w) /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ // TODO(rays) Make divisible_blob and divide_blobs members of TBLOB. bool divisible_blob(TBLOB *blob, bool italic_blob, TPOINT* location); void divide_blobs(TBLOB *blob, TBLOB *other_blob, bool italic_blob, const TPOINT& location); #endif tesseract-3.04.01/ccstruct/blread.cpp000066400000000000000000000053151266071204500174520ustar00rootroot00000000000000/********************************************************************** * File: blread.cpp (Formerly pdread.c) * Description: Friend function of BLOCK to read the uscan pd file. * Author: Ray Smith * Created: Mon Mar 18 14:39:00 GMT 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include #ifdef __UNIX__ #include #endif #include "scanutils.h" #include "fileerr.h" #include "blread.h" #define UNLV_EXT ".uzn" // unlv zone file /********************************************************************** * read_unlv_file * * Read a whole unlv zone file to make a list of blocks. **********************************************************************/ bool read_unlv_file( //print list of sides STRING name, //basename of file inT32 xsize, //image size inT32 ysize, //image size BLOCK_LIST *blocks //output list ) { FILE *pdfp; //file pointer BLOCK *block; //current block int x; //current top-down coords int y; int width; //of current block int height; BLOCK_IT block_it = blocks; //block iterator name += UNLV_EXT; //add extension if ((pdfp = fopen (name.string (), "rb")) == NULL) { return false; //didn't read one } else { while (tfscanf(pdfp, "%d %d %d %d %*s", &x, &y, &width, &height) >= 4) { //make rect block block = new BLOCK (name.string (), TRUE, 0, 0, (inT16) x, (inT16) (ysize - y - height), (inT16) (x + width), (inT16) (ysize - y)); //on end of list block_it.add_to_end (block); } fclose(pdfp); } return true; } void FullPageBlock(int width, int height, BLOCK_LIST *blocks) { BLOCK_IT block_it(blocks); BLOCK* block = new BLOCK("", TRUE, 0, 0, 0, 0, width, height); block_it.add_to_end(block); } tesseract-3.04.01/ccstruct/blread.h000066400000000000000000000026161266071204500171200ustar00rootroot00000000000000/********************************************************************** * File: blread.h (Formerly pdread.h) * Description: Friend function of BLOCK to read the uscan pd file. * Author: Ray Smith * Created: Mon Mar 18 14:39:00 GMT 1991 * * (C) Copyright 1991, 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 BLREAD_H #define BLREAD_H #include "params.h" #include "ocrblock.h" bool read_unlv_file( //print list of sides STRING name, //basename of file inT32 xsize, //image size inT32 ysize, //image size BLOCK_LIST *blocks //output list ); void FullPageBlock(int width, int height, BLOCK_LIST *blocks); #endif tesseract-3.04.01/ccstruct/boxread.cpp000066400000000000000000000221161266071204500176430ustar00rootroot00000000000000/********************************************************************** * File: boxread.cpp * Description: Read data from a box file. * Author: Ray Smith * Created: Fri Aug 24 17:47:23 PDT 2007 * * (C) Copyright 2007, 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 "boxread.h" #include #include "fileerr.h" #include "rect.h" #include "strngs.h" #include "tprintf.h" #include "unichar.h" // Special char code used to identify multi-blob labels. static const char* kMultiBlobLabelCode = "WordStr"; // Open the boxfile based on the given image filename. FILE* OpenBoxFile(const STRING& fname) { STRING filename = BoxFileName(fname); FILE* box_file = NULL; if (!(box_file = fopen(filename.string(), "rb"))) { CANTOPENFILE.error("read_next_box", TESSEXIT, "Can't open box file %s", filename.string()); } return box_file; } // Reads all boxes from the given filename. // Reads a specific target_page number if >= 0, or all pages otherwise. // Skips blanks if skip_blanks is true. // The UTF-8 label of the box is put in texts, and the full box definition as // a string is put in box_texts, with the corresponding page number in pages. // Each of the output vectors is optional (may be NULL). // Returns false if no boxes are found. bool ReadAllBoxes(int target_page, bool skip_blanks, const STRING& filename, GenericVector* boxes, GenericVector* texts, GenericVector* box_texts, GenericVector* pages) { GenericVector box_data; if (!tesseract::LoadDataFromFile(BoxFileName(filename), &box_data)) return false; return ReadMemBoxes(target_page, skip_blanks, &box_data[0], boxes, texts, box_texts, pages); } // Reads all boxes from the string. Otherwise, as ReadAllBoxes. bool ReadMemBoxes(int target_page, bool skip_blanks, const char* box_data, GenericVector* boxes, GenericVector* texts, GenericVector* box_texts, GenericVector* pages) { STRING box_str(box_data); GenericVector lines; box_str.split('\n', &lines); if (lines.empty()) return false; int num_boxes = 0; for (int i = 0; i < lines.size(); ++i) { int page = 0; STRING utf8_str; TBOX box; if (!ParseBoxFileStr(lines[i].string(), &page, &utf8_str, &box)) { continue; } if (skip_blanks && (utf8_str == " " || utf8_str == "\t")) continue; if (target_page >= 0 && page != target_page) continue; if (boxes != NULL) boxes->push_back(box); if (texts != NULL) texts->push_back(utf8_str); if (box_texts != NULL) { STRING full_text; MakeBoxFileStr(utf8_str.string(), box, target_page, &full_text); box_texts->push_back(full_text); } if (pages != NULL) pages->push_back(page); ++num_boxes; } return num_boxes > 0; } // Returns the box file name corresponding to the given image_filename. STRING BoxFileName(const STRING& image_filename) { STRING box_filename = image_filename; const char *lastdot = strrchr(box_filename.string(), '.'); if (lastdot != NULL) box_filename.truncate_at(lastdot - box_filename.string()); box_filename += ".box"; return box_filename; } // TODO(rays) convert all uses of ReadNextBox to use the new ReadAllBoxes. // Box files are used ONLY DURING TRAINING, but by both processes of // creating tr files with tesseract, and unicharset_extractor. // ReadNextBox factors out the code to interpret a line of a box // file so that applybox and unicharset_extractor interpret the same way. // This function returns the next valid box file utf8 string and coords // and returns true, or false on eof (and closes the file). // It ignores the utf8 file signature ByteOrderMark (U+FEFF=EF BB BF), checks // for valid utf-8 and allows space or tab between fields. // utf8_str is set with the unichar string, and bounding box with the box. // If there are page numbers in the file, it reads them all. bool ReadNextBox(int *line_number, FILE* box_file, STRING* utf8_str, TBOX* bounding_box) { return ReadNextBox(-1, line_number, box_file, utf8_str, bounding_box); } // As ReadNextBox above, but get a specific page number. (0-based) // Use -1 to read any page number. Files without page number all // read as if they are page 0. bool ReadNextBox(int target_page, int *line_number, FILE* box_file, STRING* utf8_str, TBOX* bounding_box) { int page = 0; char buff[kBoxReadBufSize]; // boxfile read buffer char *buffptr = buff; while (fgets(buff, sizeof(buff) - 1, box_file)) { (*line_number)++; buffptr = buff; const unsigned char *ubuf = reinterpret_cast(buffptr); if (ubuf[0] == 0xef && ubuf[1] == 0xbb && ubuf[2] == 0xbf) buffptr += 3; // Skip unicode file designation. // Check for blank lines in box file if (*buffptr == '\n' || *buffptr == '\0') continue; // Skip blank boxes. if (*buffptr == ' ' || *buffptr == '\t') continue; if (*buffptr != '\0') { if (!ParseBoxFileStr(buffptr, &page, utf8_str, bounding_box)) { tprintf("Box file format error on line %i; ignored\n", *line_number); continue; } if (target_page >= 0 && target_page != page) continue; // Not on the appropriate page. return true; // Successfully read a box. } } fclose(box_file); return false; // EOF } // Parses the given box file string into a page_number, utf8_str, and // bounding_box. Returns true on a successful parse. // The box file is assumed to contain box definitions, one per line, of the // following format for blob-level boxes: // // and for word/line-level boxes: // WordStr # // See applyybox.cpp for more information. bool ParseBoxFileStr(const char* boxfile_str, int* page_number, STRING* utf8_str, TBOX* bounding_box) { *bounding_box = TBOX(); // Initialize it to empty. *utf8_str = ""; char uch[kBoxReadBufSize]; const char *buffptr = boxfile_str; // Read the unichar without messing up on Tibetan. // According to issue 253 the utf-8 surrogates 85 and A0 are treated // as whitespace by sscanf, so it is more reliable to just find // ascii space and tab. int uch_len = 0; // Skip unicode file designation, if present. const unsigned char *ubuf = reinterpret_cast(buffptr); if (ubuf[0] == 0xef && ubuf[1] == 0xbb && ubuf[2] == 0xbf) buffptr += 3; // Allow a single blank as the UTF-8 string. Check for empty string and // then blindly eat the first character. if (*buffptr == '\0') return false; do { uch[uch_len++] = *buffptr++; } while (*buffptr != '\0' && *buffptr != ' ' && *buffptr != '\t' && uch_len < kBoxReadBufSize - 1); uch[uch_len] = '\0'; if (*buffptr != '\0') ++buffptr; int x_min, y_min, x_max, y_max; *page_number = 0; int count = sscanf(buffptr, "%d %d %d %d %d", &x_min, &y_min, &x_max, &y_max, page_number); if (count != 5 && count != 4) { tprintf("Bad box coordinates in boxfile string! %s\n", ubuf); return false; } // Test for long space-delimited string label. if (strcmp(uch, kMultiBlobLabelCode) == 0 && (buffptr = strchr(buffptr, '#')) != NULL) { strncpy(uch, buffptr + 1, kBoxReadBufSize - 1); uch[kBoxReadBufSize - 1] = '\0'; // Prevent buffer overrun. chomp_string(uch); uch_len = strlen(uch); } // Validate UTF8 by making unichars with it. int used = 0; while (used < uch_len) { UNICHAR ch(uch + used, uch_len - used); int new_used = ch.utf8_len(); if (new_used == 0) { tprintf("Bad UTF-8 str %s starts with 0x%02x at col %d\n", uch + used, uch[used], used + 1); return false; } used += new_used; } *utf8_str = uch; if (x_min > x_max) Swap(&x_min, &x_max); if (y_min > y_max) Swap(&y_min, &y_max); bounding_box->set_to_given_coords(x_min, y_min, x_max, y_max); return true; // Successfully read a box. } // Creates a box file string from a unichar string, TBOX and page number. void MakeBoxFileStr(const char* unichar_str, const TBOX& box, int page_num, STRING* box_str) { *box_str = unichar_str; box_str->add_str_int(" ", box.left()); box_str->add_str_int(" ", box.bottom()); box_str->add_str_int(" ", box.right()); box_str->add_str_int(" ", box.top()); box_str->add_str_int(" ", page_num); } tesseract-3.04.01/ccstruct/boxread.h000066400000000000000000000073671266071204500173230ustar00rootroot00000000000000/********************************************************************** * File: boxread.cpp * Description: Read data from a box file. * Author: Ray Smith * Created: Fri Aug 24 17:47:23 PDT 2007 * * (C) Copyright 2007, 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_BOXREAD_H__ #define TESSERACT_CCUTIL_BOXREAD_H__ #include #include "genericvector.h" #include "strngs.h" class STRING; class TBOX; // Size of buffer used to read a line from a box file. const int kBoxReadBufSize = 1024; // Open the boxfile based on the given image filename. // Returns NULL if the box file cannot be opened. FILE* OpenBoxFile(const STRING& fname); // Reads all boxes from the given filename. // Reads a specific target_page number if >= 0, or all pages otherwise. // Skips blanks if skip_blanks is true. // The UTF-8 label of the box is put in texts, and the full box definition as // a string is put in box_texts, with the corresponding page number in pages. // Each of the output vectors is optional (may be NULL). // Returns false if no boxes are found. bool ReadAllBoxes(int target_page, bool skip_blanks, const STRING& filename, GenericVector* boxes, GenericVector* texts, GenericVector* box_texts, GenericVector* pages); // Reads all boxes from the string. Otherwise, as ReadAllBoxes. bool ReadMemBoxes(int target_page, bool skip_blanks, const char* box_data, GenericVector* boxes, GenericVector* texts, GenericVector* box_texts, GenericVector* pages); // Returns the box file name corresponding to the given image_filename. STRING BoxFileName(const STRING& image_filename); // ReadNextBox factors out the code to interpret a line of a box // file so that applybox and unicharset_extractor interpret the same way. // This function returns the next valid box file utf8 string and coords // and returns true, or false on eof (and closes the file). // It ignores the utf8 file signature ByteOrderMark (U+FEFF=EF BB BF), checks // for valid utf-8 and allows space or tab between fields. // utf8_str is set with the unichar string, and bounding box with the box. // If there are page numbers in the file, it reads them all. bool ReadNextBox(int *line_number, FILE* box_file, STRING* utf8_str, TBOX* bounding_box); // As ReadNextBox above, but get a specific page number. (0-based) // Use -1 to read any page number. Files without page number all // read as if they are page 0. bool ReadNextBox(int target_page, int *line_number, FILE* box_file, STRING* utf8_str, TBOX* bounding_box); // Parses the given box file string into a page_number, utf8_str, and // bounding_box. Returns true on a successful parse. bool ParseBoxFileStr(const char* boxfile_str, int* page_number, STRING* utf8_str, TBOX* bounding_box); // Creates a box file string from a unichar string, TBOX and page number. void MakeBoxFileStr(const char* unichar_str, const TBOX& box, int page_num, STRING* box_str); #endif // TESSERACT_CCUTIL_BOXREAD_H__ tesseract-3.04.01/ccstruct/boxword.cpp000066400000000000000000000146431266071204500177110ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: boxword.h // Description: Class to represent the bounding boxes of the output. // Author: Ray Smith // Created: Tue May 25 14:18:14 PDT 2010 // // (C) Copyright 2010, 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 "blobs.h" #include "boxword.h" #include "normalis.h" #include "ocrblock.h" #include "pageres.h" namespace tesseract { // Clip output boxes to input blob boxes for bounds that are within this // tolerance. Otherwise, the blob may be chopped and we have to just use // the word bounding box. const int kBoxClipTolerance = 2; BoxWord::BoxWord() : length_(0) { } BoxWord::BoxWord(const BoxWord& src) { CopyFrom(src); } BoxWord::~BoxWord() { } BoxWord& BoxWord::operator=(const BoxWord& src) { CopyFrom(src); return *this; } void BoxWord::CopyFrom(const BoxWord& src) { bbox_ = src.bbox_; length_ = src.length_; boxes_.clear(); boxes_.reserve(length_); for (int i = 0; i < length_; ++i) boxes_.push_back(src.boxes_[i]); } // 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_ = tessword->NumBlobs(); // Allocate memory. boxword->boxes_.reserve(boxword->length_); 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) { EDGEPT* edgept = outline->loop; // Iterate over the edges. do { if (!edgept->IsHidden() || !edgept->prev->IsHidden()) { ICOORD pos(edgept->pos.x, edgept->pos.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; } edgept = edgept->next; } while (edgept != outline->loop); } boxword->boxes_.push_back(blob_box); } boxword->ComputeBoundingBox(); return boxword; } // 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. void BoxWord::ClipToOriginalWord(const BLOCK* block, WERD* original_word) { for (int i = 0; i < length_; ++i) { TBOX box = boxes_[i]; // Expand by a single pixel, as the poly approximation error is 1 pixel. box = TBOX(box.left() - 1, box.bottom() - 1, box.right() + 1, box.top() + 1); // Now find the original box that matches. TBOX original_box; C_BLOB_IT b_it(original_word->cblob_list()); for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { TBOX blob_box = b_it.data()->bounding_box(); if (block != NULL) blob_box.rotate(block->re_rotation()); if (blob_box.major_overlap(box)) { original_box += blob_box; } } if (!original_box.null_box()) { if (NearlyEqual(original_box.left(), box.left(), kBoxClipTolerance)) box.set_left(original_box.left()); if (NearlyEqual(original_box.right(), box.right(), kBoxClipTolerance)) box.set_right(original_box.right()); if (NearlyEqual(original_box.top(), box.top(), kBoxClipTolerance)) box.set_top(original_box.top()); if (NearlyEqual(original_box.bottom(), box.bottom(), kBoxClipTolerance)) box.set_bottom(original_box.bottom()); } original_box = original_word->bounding_box(); if (block != NULL) original_box.rotate(block->re_rotation()); boxes_[i] = box.intersection(original_box); } ComputeBoundingBox(); } // Merges the boxes from start to end, not including end, and deletes // the boxes between start and end. void BoxWord::MergeBoxes(int start, int end) { start = ClipToRange(start, 0, length_); end = ClipToRange(end, 0, length_); if (end <= start + 1) return; for (int i = start + 1; i < end; ++i) { boxes_[start] += boxes_[i]; } int shrinkage = end - 1 - start; length_ -= shrinkage; for (int i = start + 1; i < length_; ++i) boxes_[i] = boxes_[i + shrinkage]; boxes_.truncate(length_); } // Inserts a new box before the given index. // Recomputes the bounding box. void BoxWord::InsertBox(int index, const TBOX& box) { if (index < length_) boxes_.insert(box, index); else boxes_.push_back(box); length_ = boxes_.size(); ComputeBoundingBox(); } // Changes the box at the given index to the new box. // Recomputes the bounding box. void BoxWord::ChangeBox(int index, const TBOX& box) { boxes_[index] = box; ComputeBoundingBox(); } // Deletes the box with the given index, and shuffles up the rest. // Recomputes the bounding box. void BoxWord::DeleteBox(int index) { ASSERT_HOST(0 <= index && index < length_); boxes_.remove(index); --length_; ComputeBoundingBox(); } // Deletes all the boxes stored in BoxWord. void BoxWord::DeleteAllBoxes() { length_ = 0; boxes_.clear(); bbox_ = TBOX(); } // Computes the bounding box of the word. void BoxWord::ComputeBoundingBox() { bbox_ = TBOX(); for (int i = 0; i < length_; ++i) bbox_ += boxes_[i]; } // This and other putatively are the same, so call the (permanent) callback // for each blob index where the bounding boxes match. // The callback is deleted on completion. void BoxWord::ProcessMatchedBlobs(const TWERD& other, TessCallback1* cb) const { 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); } delete cb; } } // namespace tesseract. tesseract-3.04.01/ccstruct/boxword.h000066400000000000000000000061151266071204500173510ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: boxword.h // Description: Class to represent the bounding boxes of the output. // Author: Ray Smith // Created: Tue May 25 14:18:14 PDT 2010 // // (C) Copyright 2010, 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_CSTRUCT_BOXWORD_H__ #define TESSERACT_CSTRUCT_BOXWORD_H__ #include "genericvector.h" #include "rect.h" #include "unichar.h" class BLOCK; class DENORM; struct TWERD; class UNICHARSET; class WERD; class WERD_CHOICE; class WERD_RES; namespace tesseract { // Class to hold an array of bounding boxes for an output word and // the bounding box of the whole word. class BoxWord { public: BoxWord(); explicit BoxWord(const BoxWord& src); ~BoxWord(); BoxWord& operator=(const BoxWord& src); void CopyFrom(const BoxWord& src); // 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 // that overlap. If not null, the block provides the inverse rotation. void ClipToOriginalWord(const BLOCK* block, WERD* original_word); // Merges the boxes from start to end, not including end, and deletes // the boxes between start and end. void MergeBoxes(int start, int end); // Inserts a new box before the given index. // Recomputes the bounding box. void InsertBox(int index, const TBOX& box); // Changes the box at the given index to the new box. // Recomputes the bounding box. void ChangeBox(int index, const TBOX& box); // Deletes the box with the given index, and shuffles up the rest. // Recomputes the bounding box. void DeleteBox(int index); // Deletes all the boxes stored in BoxWord. void DeleteAllBoxes(); // This and other putatively are the same, so call the (permanent) callback // for each blob index where the bounding boxes match. // The callback is deleted on completion. void ProcessMatchedBlobs(const TWERD& other, TessCallback1* cb) const; const TBOX& bounding_box() const { return bbox_; } int length() const { return length_; } const TBOX& BlobBox(int index) const { return boxes_[index]; } private: void ComputeBoundingBox(); TBOX bbox_; int length_; GenericVector boxes_; }; } // namespace tesseract. #endif // TESSERACT_CSTRUCT_BOXWORD_H__ tesseract-3.04.01/ccstruct/ccstruct.cpp000066400000000000000000000024441266071204500200530ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: ccstruct.cpp // Description: ccstruct class. // Author: Samuel Charron // // (C) Copyright 2006, 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 "ccstruct.h" namespace tesseract { // APPROXIMATIONS of the fractions of the character cell taken by // the descenders, ascenders, and x-height. const double CCStruct::kDescenderFraction = 0.25; const double CCStruct::kXHeightFraction = 0.5; const double CCStruct::kAscenderFraction = 0.25; const double CCStruct::kXHeightCapRatio = CCStruct::kXHeightFraction / (CCStruct::kXHeightFraction + CCStruct::kAscenderFraction); CCStruct::CCStruct() {} CCStruct::~CCStruct() { } } tesseract-3.04.01/ccstruct/ccstruct.h000066400000000000000000000030341266071204500175140ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: ccstruct.h // Description: ccstruct class. // Author: Samuel Charron // // (C) Copyright 2006, 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_CCSTRUCT_H__ #define TESSERACT_CCSTRUCT_CCSTRUCT_H__ #include "cutil.h" namespace tesseract { class CCStruct : public CUtil { public: CCStruct(); ~CCStruct(); // Globally accessible constants. // APPROXIMATIONS of the fractions of the character cell taken by // the descenders, ascenders, and x-height. static const double kDescenderFraction; // = 0.25; static const double kXHeightFraction; // = 0.5; static const double kAscenderFraction; // = 0.25; // Derived value giving the x-height as a fraction of cap-height. static const double kXHeightCapRatio; // = XHeight/(XHeight + Ascender). }; class Tesseract; } // namespace tesseract #endif // TESSERACT_CCSTRUCT_CCSTRUCT_H__ tesseract-3.04.01/ccstruct/coutln.cpp000066400000000000000000001075351266071204500175340ustar00rootroot00000000000000/********************************************************************** * File: coutln.c (Formerly coutline.c) * Description: Code for the C_OUTLINE class. * Author: Ray Smith * Created: Mon Oct 07 16:01:57 BST 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include #ifdef __UNIX__ #include #endif #include "coutln.h" #include "allheaders.h" #include "blobs.h" #include "normalis.h" // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif ELISTIZE (C_OUTLINE) ICOORD C_OUTLINE::step_coords[4] = { ICOORD (-1, 0), ICOORD (0, -1), ICOORD (1, 0), ICOORD (0, 1) }; /** * @name C_OUTLINE::C_OUTLINE * * Constructor to build a C_OUTLINE from a CRACKEDGE LOOP. * @param startpt outline to convert * @param bot_left bounding box * @param top_right bounding box * @param length length of loop */ C_OUTLINE::C_OUTLINE (CRACKEDGE * startpt, ICOORD bot_left, ICOORD top_right, inT16 length) : box (bot_left, top_right), start (startpt->pos), offsets(NULL) { inT16 stepindex; //index to step CRACKEDGE *edgept; //current point stepcount = length; //no of steps if (length == 0) { steps = NULL; return; } //get memory steps = (uinT8 *) alloc_mem (step_mem()); memset(steps, 0, step_mem()); edgept = startpt; for (stepindex = 0; stepindex < length; stepindex++) { //set compact step set_step (stepindex, edgept->stepdir); edgept = edgept->next; } } /** * @name C_OUTLINE::C_OUTLINE * * Constructor to build a C_OUTLINE from a C_OUTLINE_FRAG. */ C_OUTLINE::C_OUTLINE ( //constructor //steps to copy ICOORD startpt, DIR128 * new_steps, inT16 length //length of loop ):start (startpt), offsets(NULL) { inT8 dirdiff; //direction difference DIR128 prevdir; //previous direction DIR128 dir; //current direction DIR128 lastdir; //dir of last step TBOX new_box; //easy bounding inT16 stepindex; //index to step inT16 srcindex; //source steps ICOORD pos; //current position pos = startpt; stepcount = length; // No. of steps. ASSERT_HOST(length >= 0); steps = reinterpret_cast(alloc_mem(step_mem())); // Get memory. memset(steps, 0, step_mem()); lastdir = new_steps[length - 1]; prevdir = lastdir; for (stepindex = 0, srcindex = 0; srcindex < length; stepindex++, srcindex++) { new_box = TBOX (pos, pos); box += new_box; //copy steps dir = new_steps[srcindex]; set_step(stepindex, dir); dirdiff = dir - prevdir; pos += step (stepindex); if ((dirdiff == 64 || dirdiff == -64) && stepindex > 0) { stepindex -= 2; //cancel there-and-back prevdir = stepindex >= 0 ? step_dir (stepindex) : lastdir; } else prevdir = dir; } ASSERT_HOST (pos.x () == startpt.x () && pos.y () == startpt.y ()); do { dirdiff = step_dir (stepindex - 1) - step_dir (0); if (dirdiff == 64 || dirdiff == -64) { start += step (0); stepindex -= 2; //cancel there-and-back for (int i = 0; i < stepindex; ++i) set_step(i, step_dir(i + 1)); } } while (stepindex > 1 && (dirdiff == 64 || dirdiff == -64)); stepcount = stepindex; ASSERT_HOST (stepcount >= 4); } /** * @name C_OUTLINE::C_OUTLINE * * Constructor to build a C_OUTLINE from a rotation of a C_OUTLINE. * @param srcline outline to rotate * @param rotation rotate to coord */ C_OUTLINE::C_OUTLINE(C_OUTLINE *srcline, FCOORD rotation) : offsets(NULL) { TBOX new_box; //easy bounding inT16 stepindex; //index to step inT16 dirdiff; //direction change ICOORD pos; //current position ICOORD prevpos; //previous dest point ICOORD destpos; //destination point inT16 destindex; //index to step DIR128 dir; //coded direction uinT8 new_step; stepcount = srcline->stepcount * 2; if (stepcount == 0) { steps = NULL; box = srcline->box; box.rotate(rotation); return; } //get memory steps = (uinT8 *) alloc_mem (step_mem()); memset(steps, 0, step_mem()); for (int iteration = 0; iteration < 2; ++iteration) { DIR128 round1 = iteration == 0 ? 32 : 0; DIR128 round2 = iteration != 0 ? 32 : 0; pos = srcline->start; prevpos = pos; prevpos.rotate (rotation); start = prevpos; box = TBOX (start, start); destindex = 0; for (stepindex = 0; stepindex < srcline->stepcount; stepindex++) { pos += srcline->step (stepindex); destpos = pos; destpos.rotate (rotation); // tprintf("%i %i %i %i ", destpos.x(), destpos.y(), pos.x(), pos.y()); while (destpos.x () != prevpos.x () || destpos.y () != prevpos.y ()) { dir = DIR128 (FCOORD (destpos - prevpos)); dir += 64; //turn to step style new_step = dir.get_dir (); // tprintf(" %i\n", new_step); if (new_step & 31) { set_step(destindex++, dir + round1); prevpos += step(destindex - 1); if (destindex < 2 || ((dirdiff = step_dir (destindex - 1) - step_dir (destindex - 2)) != -64 && dirdiff != 64)) { set_step(destindex++, dir + round2); prevpos += step(destindex - 1); } else { prevpos -= step(destindex - 1); destindex--; prevpos -= step(destindex - 1); set_step(destindex - 1, dir + round2); prevpos += step(destindex - 1); } } else { set_step(destindex++, dir); prevpos += step(destindex - 1); } while (destindex >= 2 && ((dirdiff = step_dir (destindex - 1) - step_dir (destindex - 2)) == -64 || dirdiff == 64)) { prevpos -= step(destindex - 1); prevpos -= step(destindex - 2); destindex -= 2; // Forget u turn } //ASSERT_HOST(prevpos.x() == destpos.x() && prevpos.y() == destpos.y()); new_box = TBOX (destpos, destpos); box += new_box; } } ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ()); dirdiff = step_dir (destindex - 1) - step_dir (0); while ((dirdiff == 64 || dirdiff == -64) && destindex > 1) { start += step (0); destindex -= 2; for (int i = 0; i < destindex; ++i) set_step(i, step_dir(i + 1)); dirdiff = step_dir (destindex - 1) - step_dir (0); } if (destindex >= 4) break; } ASSERT_HOST(destindex <= stepcount); stepcount = destindex; destpos = start; for (stepindex = 0; stepindex < stepcount; stepindex++) { destpos += step (stepindex); } ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ()); } // Build a fake outline, given just a bounding box and append to the list. void C_OUTLINE::FakeOutline(const TBOX& box, C_OUTLINE_LIST* outlines) { C_OUTLINE_IT ol_it(outlines); // Make a C_OUTLINE from the bounds. This is a bit of a hack, // as there is no outline, just a bounding box, but it works nicely. CRACKEDGE start; start.pos = box.topleft(); C_OUTLINE* outline = new C_OUTLINE(&start, box.topleft(), box.botright(), 0); ol_it.add_to_end(outline); } /** * @name C_OUTLINE::area * * Compute the area of the outline. */ inT32 C_OUTLINE::area() const { int stepindex; //current step inT32 total_steps; //steps to do inT32 total; //total area ICOORD pos; //position of point ICOORD next_step; //step to next pix // We aren't going to modify the list, or its contents, but there is // no const iterator. C_OUTLINE_IT it(const_cast(&children)); pos = start_pos (); total_steps = pathlength (); total = 0; for (stepindex = 0; stepindex < total_steps; stepindex++) { //all intersected next_step = step (stepindex); if (next_step.x () < 0) total += pos.y (); else if (next_step.x () > 0) total -= pos.y (); pos += next_step; } for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) total += it.data ()->area ();//add areas of children return total; } /** * @name C_OUTLINE::perimeter * * Compute the perimeter of the outline and its first level children. */ inT32 C_OUTLINE::perimeter() const { inT32 total_steps; // Return value. // We aren't going to modify the list, or its contents, but there is // no const iterator. C_OUTLINE_IT it(const_cast(&children)); total_steps = pathlength(); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) total_steps += it.data()->pathlength(); // Add perimeters of children. return total_steps; } /** * @name C_OUTLINE::outer_area * * Compute the area of the outline. */ inT32 C_OUTLINE::outer_area() const { int stepindex; //current step inT32 total_steps; //steps to do inT32 total; //total area ICOORD pos; //position of point ICOORD next_step; //step to next pix pos = start_pos (); total_steps = pathlength (); if (total_steps == 0) return box.area(); total = 0; for (stepindex = 0; stepindex < total_steps; stepindex++) { //all intersected next_step = step (stepindex); if (next_step.x () < 0) total += pos.y (); else if (next_step.x () > 0) total -= pos.y (); pos += next_step; } return total; } /** * @name C_OUTLINE::count_transitions * * Compute the number of x and y maxes and mins in the outline. * @param threshold winding number on size */ inT32 C_OUTLINE::count_transitions(inT32 threshold) { BOOL8 first_was_max_x; //what was first BOOL8 first_was_max_y; BOOL8 looking_for_max_x; //what is next BOOL8 looking_for_min_x; BOOL8 looking_for_max_y; //what is next BOOL8 looking_for_min_y; int stepindex; //current step inT32 total_steps; //steps to do //current limits inT32 max_x, min_x, max_y, min_y; inT32 initial_x, initial_y; //initial limits inT32 total; //total changes ICOORD pos; //position of point ICOORD next_step; //step to next pix pos = start_pos (); total_steps = pathlength (); total = 0; max_x = min_x = pos.x (); max_y = min_y = pos.y (); looking_for_max_x = TRUE; looking_for_min_x = TRUE; looking_for_max_y = TRUE; looking_for_min_y = TRUE; first_was_max_x = FALSE; first_was_max_y = FALSE; initial_x = pos.x (); initial_y = pos.y (); //stop uninit warning for (stepindex = 0; stepindex < total_steps; stepindex++) { //all intersected next_step = step (stepindex); pos += next_step; if (next_step.x () < 0) { if (looking_for_max_x && pos.x () < min_x) min_x = pos.x (); if (looking_for_min_x && max_x - pos.x () > threshold) { if (looking_for_max_x) { initial_x = max_x; first_was_max_x = FALSE; } total++; looking_for_max_x = TRUE; looking_for_min_x = FALSE; min_x = pos.x (); //reset min } } else if (next_step.x () > 0) { if (looking_for_min_x && pos.x () > max_x) max_x = pos.x (); if (looking_for_max_x && pos.x () - min_x > threshold) { if (looking_for_min_x) { initial_x = min_x; //remember first min first_was_max_x = TRUE; } total++; looking_for_max_x = FALSE; looking_for_min_x = TRUE; max_x = pos.x (); } } else if (next_step.y () < 0) { if (looking_for_max_y && pos.y () < min_y) min_y = pos.y (); if (looking_for_min_y && max_y - pos.y () > threshold) { if (looking_for_max_y) { initial_y = max_y; //remember first max first_was_max_y = FALSE; } total++; looking_for_max_y = TRUE; looking_for_min_y = FALSE; min_y = pos.y (); //reset min } } else { if (looking_for_min_y && pos.y () > max_y) max_y = pos.y (); if (looking_for_max_y && pos.y () - min_y > threshold) { if (looking_for_min_y) { initial_y = min_y; //remember first min first_was_max_y = TRUE; } total++; looking_for_max_y = FALSE; looking_for_min_y = TRUE; max_y = pos.y (); } } } if (first_was_max_x && looking_for_min_x) { if (max_x - initial_x > threshold) total++; else total--; } else if (!first_was_max_x && looking_for_max_x) { if (initial_x - min_x > threshold) total++; else total--; } if (first_was_max_y && looking_for_min_y) { if (max_y - initial_y > threshold) total++; else total--; } else if (!first_was_max_y && looking_for_max_y) { if (initial_y - min_y > threshold) total++; else total--; } return total; } /** * @name C_OUTLINE::operator< * * @return TRUE if the left operand is inside the right one. * @param other other outline */ BOOL8 C_OUTLINE::operator< (const C_OUTLINE & other) const { inT16 count = 0; //winding count ICOORD pos; //position of point inT32 stepindex; //index to cstep if (!box.overlap (other.box)) return FALSE; //can't be contained if (stepcount == 0) return other.box.contains(this->box); pos = start; for (stepindex = 0; stepindex < stepcount && (count = other.winding_number (pos)) == INTERSECTING; stepindex++) pos += step (stepindex); //try all points if (count == INTERSECTING) { //all intersected pos = other.start; for (stepindex = 0; stepindex < other.stepcount && (count = winding_number (pos)) == INTERSECTING; stepindex++) //try other way round pos += other.step (stepindex); return count == INTERSECTING || count == 0; } return count != 0; } /** * @name C_OUTLINE::winding_number * * @return the winding number of the outline around the given point. * @param point point to wind around */ inT16 C_OUTLINE::winding_number(ICOORD point) const { inT16 stepindex; //index to cstep inT16 count; //winding count ICOORD vec; //to current point ICOORD stepvec; //step vector inT32 cross; //cross product vec = start - point; //vector to it count = 0; for (stepindex = 0; stepindex < stepcount; stepindex++) { stepvec = step (stepindex); //get the step //crossing the line if (vec.y () <= 0 && vec.y () + stepvec.y () > 0) { cross = vec * stepvec; //cross product if (cross > 0) count++; //crossing right half else if (cross == 0) return INTERSECTING; //going through point } else if (vec.y () > 0 && vec.y () + stepvec.y () <= 0) { cross = vec * stepvec; if (cross < 0) count--; //crossing back else if (cross == 0) return INTERSECTING; //illegal } vec += stepvec; //sum vectors } return count; //winding number } /** * C_OUTLINE::turn_direction * * @return the sum direction delta of the outline. */ inT16 C_OUTLINE::turn_direction() const { //winding number DIR128 prevdir; //previous direction DIR128 dir; //current direction inT16 stepindex; //index to cstep inT8 dirdiff; //direction difference inT16 count; //winding count if (stepcount == 0) return 128; count = 0; prevdir = step_dir (stepcount - 1); for (stepindex = 0; stepindex < stepcount; stepindex++) { dir = step_dir (stepindex); dirdiff = dir - prevdir; ASSERT_HOST (dirdiff == 0 || dirdiff == 32 || dirdiff == -32); count += dirdiff; prevdir = dir; } ASSERT_HOST (count == 128 || count == -128); return count; //winding number } /** * @name C_OUTLINE::reverse * * Reverse the direction of an outline. */ void C_OUTLINE::reverse() { //reverse drection DIR128 halfturn = MODULUS / 2; //amount to shift DIR128 stepdir; //direction of step inT16 stepindex; //index to cstep inT16 farindex; //index to other side inT16 halfsteps; //half of stepcount halfsteps = (stepcount + 1) / 2; for (stepindex = 0; stepindex < halfsteps; stepindex++) { farindex = stepcount - stepindex - 1; stepdir = step_dir (stepindex); set_step (stepindex, step_dir (farindex) + halfturn); set_step (farindex, stepdir + halfturn); } } /** * @name C_OUTLINE::move * * Move C_OUTLINE by vector * @param vec vector to reposition OUTLINE by */ void C_OUTLINE::move(const ICOORD vec) { C_OUTLINE_IT it(&children); // iterator box.move (vec); start += vec; for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) it.data ()->move (vec); // move child outlines } /** * Returns true if *this and its children are legally nested. * The outer area of a child should have the opposite sign to the * parent. If not, it means we have discarded an outline in between * (probably due to excessive length). */ bool C_OUTLINE::IsLegallyNested() const { if (stepcount == 0) return true; int parent_area = outer_area(); // We aren't going to modify the list, or its contents, but there is // no const iterator. C_OUTLINE_IT child_it(const_cast(&children)); for (child_it.mark_cycle_pt(); !child_it.cycled_list(); child_it.forward()) { const C_OUTLINE* child = child_it.data(); if (child->outer_area() * parent_area > 0 || !child->IsLegallyNested()) return false; } return true; } /** * If this outline is smaller than the given min_size, delete this and * remove from its list, via *it, after checking that *it points to this. * Otherwise, if any children of this are too small, delete them. * On entry, *it must be an iterator pointing to this. If this gets deleted * then this is extracted from *it, so an iteration can continue. * @param min_size minimum size for outline * @param it outline iterator */ void C_OUTLINE::RemoveSmallRecursive(int min_size, C_OUTLINE_IT* it) { if (box.width() < min_size || box.height() < min_size) { ASSERT_HOST(this == it->data()); delete it->extract(); // Too small so get rid of it and any children. } else if (!children.empty()) { // Search the children of this, deleting any that are too small. C_OUTLINE_IT child_it(&children); for (child_it.mark_cycle_pt(); !child_it.cycled_list(); child_it.forward()) { C_OUTLINE* child = child_it.data(); child->RemoveSmallRecursive(min_size, &child_it); } } } // Factored out helpers below are used only by ComputeEdgeOffsets to operate // on data from an 8-bit Pix, and assume that any input x and/or y are already // constrained to be legal Pix coordinates. /** * Helper computes the local 2-D gradient (dx, dy) from the 2x2 cell centered * on the given (x,y). If the cell would go outside the image, it is padded * with white. */ static void ComputeGradient(const l_uint32* data, int wpl, int x, int y, int width, int height, ICOORD* gradient) { const l_uint32* line = data + y * wpl; int pix_x_y = x < width && y < height ? GET_DATA_BYTE(const_cast (reinterpret_cast(line)), x) : 255; int pix_x_prevy = x < width && y > 0 ? GET_DATA_BYTE(const_cast (reinterpret_cast(line - wpl)), x) : 255; int pix_prevx_prevy = x > 0 && y > 0 ? GET_DATA_BYTE(const_cast (reinterpret_cast(line - wpl)), x - 1) : 255; int pix_prevx_y = x > 0 && y < height ? GET_DATA_BYTE(const_cast (reinterpret_cast(line)), x - 1) : 255; gradient->set_x(pix_x_y + pix_x_prevy - (pix_prevx_y + pix_prevx_prevy)); gradient->set_y(pix_x_prevy + pix_prevx_prevy - (pix_x_y + pix_prevx_y)); } /** * Helper evaluates a vertical difference, (x,y) - (x,y-1), returning true if * the difference, matches diff_sign and updating the best_diff, best_sum, * best_y if a new max. */ static bool EvaluateVerticalDiff(const l_uint32* data, int wpl, int diff_sign, int x, int y, int height, int* best_diff, int* best_sum, int* best_y) { if (y <= 0 || y >= height) return false; const l_uint32* line = data + y * wpl; int pixel1 = GET_DATA_BYTE(const_cast (reinterpret_cast(line - wpl)), x); int pixel2 = GET_DATA_BYTE(const_cast (reinterpret_cast(line)), x); int diff = (pixel2 - pixel1) * diff_sign; if (diff > *best_diff) { *best_diff = diff; *best_sum = pixel1 + pixel2; *best_y = y; } return diff > 0; } /** * Helper evaluates a horizontal difference, (x,y) - (x-1,y), where y is implied * by the input image line, returning true if the difference matches diff_sign * and updating the best_diff, best_sum, best_x if a new max. */ static bool EvaluateHorizontalDiff(const l_uint32* line, int diff_sign, int x, int width, int* best_diff, int* best_sum, int* best_x) { if (x <= 0 || x >= width) return false; int pixel1 = GET_DATA_BYTE(const_cast (reinterpret_cast(line)), x - 1); int pixel2 = GET_DATA_BYTE(const_cast (reinterpret_cast(line)), x); int diff = (pixel2 - pixel1) * diff_sign; if (diff > *best_diff) { *best_diff = diff; *best_sum = pixel1 + pixel2; *best_x = x; } return diff > 0; } /** * Adds sub-pixel resolution EdgeOffsets for the outline if the supplied * pix is 8-bit. Does nothing otherwise. * Operation: Consider the following near-horizontal line: * @verbatim * _________ * |________ * |________ * @endverbatim * At *every* position along this line, the gradient direction will be close * to vertical. Extrapoaltion/interpolation of the position of the threshold * that was used to binarize the image gives a more precise vertical position * for each horizontal step, and the conflict in step direction and gradient * direction can be used to ignore the vertical steps. */ void C_OUTLINE::ComputeEdgeOffsets(int threshold, Pix* pix) { if (pixGetDepth(pix) != 8) return; const l_uint32* data = pixGetData(pix); int wpl = pixGetWpl(pix); int width = pixGetWidth(pix); int height = pixGetHeight(pix); bool negative = flag(COUT_INVERSE); delete [] offsets; offsets = new EdgeOffset[stepcount]; ICOORD pos = start; ICOORD prev_gradient; ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height, &prev_gradient); for (int s = 0; s < stepcount; ++s) { ICOORD step_vec = step(s); TPOINT pt1(pos); pos += step_vec; TPOINT pt2(pos); ICOORD next_gradient; ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height, &next_gradient); // Use the sum of the prev and next as the working gradient. ICOORD gradient = prev_gradient + next_gradient; // best_diff will be manipulated to be always positive. int best_diff = 0; // offset will be the extrapolation of the location of the greyscale // threshold from the edge with the largest difference, relative to the // location of the binary edge. int offset = 0; if (pt1.y == pt2.y && abs(gradient.y()) * 2 >= abs(gradient.x())) { // Horizontal step. diff_sign == 1 indicates black above. int diff_sign = (pt1.x > pt2.x) == negative ? 1 : -1; int x = MIN(pt1.x, pt2.x); int y = height - pt1.y; int best_sum = 0; int best_y = y; EvaluateVerticalDiff(data, wpl, diff_sign, x, y, height, &best_diff, &best_sum, &best_y); // Find the strongest edge. int test_y = y; do { ++test_y; } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height, &best_diff, &best_sum, &best_y)); test_y = y; do { --test_y; } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height, &best_diff, &best_sum, &best_y)); offset = diff_sign * (best_sum / 2 - threshold) + (y - best_y) * best_diff; } else if (pt1.x == pt2.x && abs(gradient.x()) * 2 >= abs(gradient.y())) { // Vertical step. diff_sign == 1 indicates black on the left. int diff_sign = (pt1.y > pt2.y) == negative ? 1 : -1; int x = pt1.x; int y = height - MAX(pt1.y, pt2.y); const l_uint32* line = pixGetData(pix) + y * wpl; int best_sum = 0; int best_x = x; EvaluateHorizontalDiff(line, diff_sign, x, width, &best_diff, &best_sum, &best_x); // Find the strongest edge. int test_x = x; do { ++test_x; } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width, &best_diff, &best_sum, &best_x)); test_x = x; do { --test_x; } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width, &best_diff, &best_sum, &best_x)); offset = diff_sign * (threshold - best_sum / 2) + (best_x - x) * best_diff; } offsets[s].offset_numerator = static_cast(ClipToRange(offset, -MAX_INT8, MAX_INT8)); offsets[s].pixel_diff = static_cast(ClipToRange(best_diff, 0 , MAX_UINT8)); if (negative) gradient = -gradient; // Compute gradient angle quantized to 256 directions, rotated by 64 (pi/2) // to convert from gradient direction to edge direction. offsets[s].direction = Modulo(FCOORD::binary_angle_plus_pi(gradient.angle()) + 64, 256); prev_gradient = next_gradient; } } /** * Adds sub-pixel resolution EdgeOffsets for the outline using only * a binary image source. * * Runs a sliding window of 5 edge steps over the outline, maintaining a count * of the number of steps in each of the 4 directions in the window, and a * sum of the x or y position of each step (as appropriate to its direction.) * Ignores single-count steps EXCEPT the sharp U-turn and smoothes out the * perpendicular direction. Eg * @verbatim * ___ ___ Chain code from the left: * |___ ___ ___| 222122212223221232223000 * |___| |_| Corresponding counts of each direction: * 0 00000000000000000123 * 1 11121111001111100000 * 2 44434443443333343321 * 3 00000001111111112111 * Count of direction at center 41434143413313143313 * Step gets used? YNYYYNYYYNYYNYNYYYyY (y= U-turn exception) * Path redrawn showing only the used points: * ___ ___ * ___ ___ ___| * ___ _ * @endverbatim * Sub-pixel edge position cannot be shown well with ASCII-art, but each * horizontal step's y position is the mean of the y positions of the steps * in the same direction in the sliding window, which makes a much smoother * outline, without losing important detail. */ void C_OUTLINE::ComputeBinaryOffsets() { delete [] offsets; offsets = new EdgeOffset[stepcount]; // Count of the number of steps in each direction in the sliding window. int dir_counts[4]; // Sum of the positions (y for a horizontal step, x for vertical) in each // direction in the sliding window. int pos_totals[4]; memset(dir_counts, 0, sizeof(dir_counts)); memset(pos_totals, 0, sizeof(pos_totals)); ICOORD pos = start; ICOORD tail_pos = pos; // tail_pos is the trailing position, with the next point to be lost from // the window. tail_pos -= step(stepcount - 1); tail_pos -= step(stepcount - 2); // head_pos is the leading position, with the next point to be added to the // window. ICOORD head_pos = tail_pos; // Set up the initial window with 4 points in [-2, 2) for (int s = -2; s < 2; ++s) { increment_step(s, 1, &head_pos, dir_counts, pos_totals); } for (int s = 0; s < stepcount; pos += step(s++)) { // At step s, s in in the middle of [s-2, s+2]. increment_step(s + 2, 1, &head_pos, dir_counts, pos_totals); int dir_index = chain_code(s); ICOORD step_vec = step(s); int best_diff = 0; int offset = 0; // Use only steps that have a count of >=2 OR the strong U-turn with a // single d and 2 at d-1 and 2 at d+1 (mod 4). if (dir_counts[dir_index] >= 2 || (dir_counts[dir_index] == 1 && dir_counts[Modulo(dir_index - 1, 4)] == 2 && dir_counts[Modulo(dir_index + 1, 4)] == 2)) { // Valid step direction. best_diff = dir_counts[dir_index]; int edge_pos = step_vec.x() == 0 ? pos.x() : pos.y(); // The offset proposes that the actual step should be positioned at // the mean position of the steps in the window of the same direction. // See ASCII art above. offset = pos_totals[dir_index] - best_diff * edge_pos; } offsets[s].offset_numerator = static_cast(ClipToRange(offset, -MAX_INT8, MAX_INT8)); offsets[s].pixel_diff = static_cast(ClipToRange(best_diff, 0 , MAX_UINT8)); // The direction is just the vector from start to end of the window. FCOORD direction(head_pos.x() - tail_pos.x(), head_pos.y() - tail_pos.y()); offsets[s].direction = direction.to_direction(); increment_step(s - 2, -1, &tail_pos, dir_counts, pos_totals); } } /** * Renders the outline to the given pix, with left and top being * the coords of the upper-left corner of the pix. */ void C_OUTLINE::render(int left, int top, Pix* pix) const { ICOORD pos = start; for (int stepindex = 0; stepindex < stepcount; ++stepindex) { ICOORD next_step = step(stepindex); if (next_step.y() < 0) { pixRasterop(pix, 0, top - pos.y(), pos.x() - left, 1, PIX_NOT(PIX_DST), NULL, 0, 0); } else if (next_step.y() > 0) { pixRasterop(pix, 0, top - pos.y() - 1, pos.x() - left, 1, PIX_NOT(PIX_DST), NULL, 0, 0); } pos += next_step; } } /** * Renders just the outline to the given pix (no fill), with left and top * being the coords of the upper-left corner of the pix. * @param left coord * @param top coord * @param pix the pix to outline */ void C_OUTLINE::render_outline(int left, int top, Pix* pix) const { ICOORD pos = start; for (int stepindex = 0; stepindex < stepcount; ++stepindex) { ICOORD next_step = step(stepindex); if (next_step.y() < 0) { pixSetPixel(pix, pos.x() - left, top - pos.y(), 1); } else if (next_step.y() > 0) { pixSetPixel(pix, pos.x() - left - 1, top - pos.y() - 1, 1); } else if (next_step.x() < 0) { pixSetPixel(pix, pos.x() - left - 1, top - pos.y(), 1); } else if (next_step.x() > 0) { pixSetPixel(pix, pos.x() - left, top - pos.y() - 1, 1); } pos += next_step; } } /** * @name C_OUTLINE::plot * * Draw the outline in the given colour. * @param window window to draw in * @param colour colour to draw in */ #ifndef GRAPHICS_DISABLED void C_OUTLINE::plot(ScrollView* window, ScrollView::Color colour) const { inT16 stepindex; // index to cstep ICOORD pos; // current position DIR128 stepdir; // direction of step pos = start; // current position window->Pen(colour); if (stepcount == 0) { window->Rectangle(box.left(), box.top(), box.right(), box.bottom()); return; } window->SetCursor(pos.x(), pos.y()); stepindex = 0; while (stepindex < stepcount) { pos += step(stepindex); // step to next stepdir = step_dir(stepindex); stepindex++; // count steps // merge straight lines while (stepindex < stepcount && stepdir.get_dir() == step_dir(stepindex).get_dir()) { pos += step(stepindex); stepindex++; } window->DrawTo(pos.x(), pos.y()); } } /** * Draws the outline in the given colour, normalized using the given denorm, * making use of sub-pixel accurate information if available. */ void C_OUTLINE::plot_normed(const DENORM& denorm, ScrollView::Color colour, ScrollView* window) const { window->Pen(colour); if (stepcount == 0) { window->Rectangle(box.left(), box.top(), box.right(), box.bottom()); return; } const DENORM* root_denorm = denorm.RootDenorm(); ICOORD pos = start; // current position FCOORD f_pos = sub_pixel_pos_at_index(pos, 0); FCOORD pos_normed; denorm.NormTransform(root_denorm, f_pos, &pos_normed); window->SetCursor(IntCastRounded(pos_normed.x()), IntCastRounded(pos_normed.y())); for (int s = 0; s < stepcount; pos += step(s++)) { int edge_weight = edge_strength_at_index(s); if (edge_weight == 0) { // This point has conflicting gradient and step direction, so ignore it. continue; } FCOORD f_pos = sub_pixel_pos_at_index(pos, s); FCOORD pos_normed; denorm.NormTransform(root_denorm, f_pos, &pos_normed); window->DrawTo(IntCastRounded(pos_normed.x()), IntCastRounded(pos_normed.y())); } } #endif /** * @name C_OUTLINE::operator= * * Assignment - deep copy data * @param source assign from this */ C_OUTLINE & C_OUTLINE::operator= (const C_OUTLINE & source) { box = source.box; start = source.start; if (steps != NULL) free_mem(steps); stepcount = source.stepcount; steps = (uinT8 *) alloc_mem (step_mem()); memmove (steps, source.steps, step_mem()); if (!children.empty ()) children.clear (); children.deep_copy(&source.children, &deep_copy); delete [] offsets; if (source.offsets != NULL) { offsets = new EdgeOffset[stepcount]; memcpy(offsets, source.offsets, stepcount * sizeof(*offsets)); } else { offsets = NULL; } return *this; } /** * Helper for ComputeBinaryOffsets. Increments pos, dir_counts, pos_totals * by the step, increment, and vertical step ? x : y position * increment * at step s Mod stepcount respectively. Used to add or subtract the * direction and position to/from accumulators of a small neighbourhood. */ void C_OUTLINE::increment_step(int s, int increment, ICOORD* pos, int* dir_counts, int* pos_totals) const { int step_index = Modulo(s, stepcount); int dir_index = chain_code(step_index); dir_counts[dir_index] += increment; ICOORD step_vec = step(step_index); if (step_vec.x() == 0) pos_totals[dir_index] += pos->x() * increment; else pos_totals[dir_index] += pos->y() * increment; *pos += step_vec; } ICOORD C_OUTLINE::chain_step(int chaindir) { return step_coords[chaindir % 4]; } tesseract-3.04.01/ccstruct/coutln.h000066400000000000000000000274561266071204500172040ustar00rootroot00000000000000/********************************************************************** * File: coutln.c (Formerly: coutline.c) * Description: Code for the C_OUTLINE class. * Author: Ray Smith * Created: Mon Oct 07 16:01:57 BST 1991 * * (C) Copyright 1991, 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 COUTLN_H #define COUTLN_H #include "crakedge.h" #include "mod128.h" #include "bits16.h" #include "rect.h" #include "blckerr.h" #include "scrollview.h" class DENORM; #define INTERSECTING MAX_INT16//no winding number //mask to get step #define STEP_MASK 3 enum C_OUTLINE_FLAGS { COUT_INVERSE //White on black blob }; // Simple struct to hold the 3 values needed to compute a more precise edge // position and direction. The offset_numerator is the difference between the // grey threshold and the mean pixel value. pixel_diff is the difference between // the pixels in the edge. Consider the following row of pixels: p1 p2 p3 p4 p5 // Say the image was thresholded at threshold t, making p1, p2, p3 black // and p4, p5 white (p1, p2, p3 < t, and p4, p5 >= t), but suppose that // max(p[i+1] - p[i]) is p3 - p2. Then the extrapolated position of the edge, // based on the maximum gradient, is at the crack between p2 and p3 plus the // offset (t - (p2+p3)/2)/(p3 - p2). We store the pixel difference p3-p2 // denominator in pixel_diff and the offset numerator, relative to the original // binary edge (t - (p2+p3)/2) - (p3 -p2) in offset_numerator. // The sign of offset_numerator and pixel_diff are manipulated to ensure // that the pixel_diff, which will be used as a weight, is always positive. // The direction stores the quantized feature direction for the given step // computed from the edge gradient. (Using binary_angle_plus_pi.) // If the pixel_diff is zero, it means that the direction of the gradient // is in conflict with the step direction, so this step is to be ignored. struct EdgeOffset { inT8 offset_numerator; uinT8 pixel_diff; uinT8 direction; }; class DLLSYM C_OUTLINE; //forward declaration struct Pix; ELISTIZEH (C_OUTLINE) class DLLSYM C_OUTLINE:public ELIST_LINK { public: C_OUTLINE() { //empty constructor steps = NULL; offsets = NULL; } C_OUTLINE( //constructor CRACKEDGE *startpt, //from edge detector ICOORD bot_left, //bounding box //length of loop ICOORD top_right, inT16 length); C_OUTLINE(ICOORD startpt, //start of loop DIR128 *new_steps, //steps in loop inT16 length); //length of loop //outline to copy C_OUTLINE(C_OUTLINE *srcline, FCOORD rotation); //and rotate // Build a fake outline, given just a bounding box and append to the list. static void FakeOutline(const TBOX& box, C_OUTLINE_LIST* outlines); ~C_OUTLINE () { //destructor if (steps != NULL) free_mem(steps); steps = NULL; delete [] offsets; } BOOL8 flag( //test flag C_OUTLINE_FLAGS mask) const { //flag to test return flags.bit (mask); } void set_flag( //set flag value C_OUTLINE_FLAGS mask, //flag to test BOOL8 value) { //value to set flags.set_bit (mask, value); } C_OUTLINE_LIST *child() { //get child list return &children; } //access function const TBOX &bounding_box() const { return box; } void set_step( //set a step inT16 stepindex, //index of step inT8 stepdir) { //chain code int shift = stepindex%4 * 2; uinT8 mask = 3 << shift; steps[stepindex/4] = ((stepdir << shift) & mask) | (steps[stepindex/4] & ~mask); //squeeze 4 into byte } void set_step( //set a step inT16 stepindex, //index of step DIR128 stepdir) { //direction //clean it inT8 chaindir = stepdir.get_dir() >> (DIRBITS - 2); //difference set_step(stepindex, chaindir); //squeeze 4 into byte } inT32 pathlength() const { //get path length return stepcount; } // Return step at a given index as a DIR128. DIR128 step_dir(int index) const { return DIR128((inT16)(((steps[index/4] >> (index%4 * 2)) & STEP_MASK) << (DIRBITS - 2))); } // Return the step vector for the given outline position. ICOORD step(int index) const { // index of step return step_coords[chain_code(index)]; } // get start position const ICOORD &start_pos() const { return start; } // Returns the position at the given index on the outline. // NOT to be used lightly, as it has to iterate the outline to find out. ICOORD position_at_index(int index) const { ICOORD pos = start; for (int i = 0; i < index; ++i) pos += step(i); return pos; } // Returns the sub-pixel accurate position given the integer position pos // at the given index on the outline. pos may be a return value of // position_at_index, or computed by repeatedly adding step to the // start_pos() in the usual way. FCOORD sub_pixel_pos_at_index(const ICOORD& pos, int index) const { const ICOORD& step_to_next(step(index)); FCOORD f_pos(pos.x() + step_to_next.x() / 2.0f, pos.y() + step_to_next.y() / 2.0f); if (offsets != NULL && offsets[index].pixel_diff > 0) { float offset = offsets[index].offset_numerator; offset /= offsets[index].pixel_diff; if (step_to_next.x() != 0) f_pos.set_y(f_pos.y() + offset); else f_pos.set_x(f_pos.x() + offset); } return f_pos; } // Returns the step direction for the given index or -1 if there is none. int direction_at_index(int index) const { if (offsets != NULL && offsets[index].pixel_diff > 0) return offsets[index].direction; return -1; } // Returns the edge strength for the given index. // If there are no recorded edge strengths, returns 1 (assuming the image // is binary). Returns 0 if the gradient direction conflicts with the // step direction, indicating that this position could be skipped. int edge_strength_at_index(int index) const { if (offsets != NULL) return offsets[index].pixel_diff; return 1; } // Return the step as a chain code (0-3) related to the standard feature // direction of binary_angle_plus_pi by: // chain_code * 64 = feature direction. int chain_code(int index) const { // index of step return (steps[index / 4] >> (index % 4 * 2)) & STEP_MASK; } inT32 area() const; // Returns area of self and 1st level children. inT32 perimeter() const; // Total perimeter of self and 1st level children. inT32 outer_area() const; // Returns area of self only. inT32 count_transitions( //count maxima inT32 threshold); //size threshold BOOL8 operator< ( //containment test const C_OUTLINE & other) const; BOOL8 operator> ( //containment test C_OUTLINE & other) const { return other < *this; //use the < to do it } inT16 winding_number( //get winding number ICOORD testpt) const; //around this point //get direction inT16 turn_direction() const; void reverse(); //reverse direction void move( // reposition outline const ICOORD vec); // by vector // Returns true if *this and its children are legally nested. // The outer area of a child should have the opposite sign to the // parent. If not, it means we have discarded an outline in between // (probably due to excessive length). bool IsLegallyNested() const; // If this outline is smaller than the given min_size, delete this and // remove from its list, via *it, after checking that *it points to this. // Otherwise, if any children of this are too small, delete them. // On entry, *it must be an iterator pointing to this. If this gets deleted // then this is extracted from *it, so an iteration can continue. void RemoveSmallRecursive(int min_size, C_OUTLINE_IT* it); // Adds sub-pixel resolution EdgeOffsets for the outline if the supplied // pix is 8-bit. Does nothing otherwise. void ComputeEdgeOffsets(int threshold, Pix* pix); // Adds sub-pixel resolution EdgeOffsets for the outline using only // a binary image source. void ComputeBinaryOffsets(); // Renders the outline to the given pix, with left and top being // the coords of the upper-left corner of the pix. void render(int left, int top, Pix* pix) const; // Renders just the outline to the given pix (no fill), with left and top // being the coords of the upper-left corner of the pix. void render_outline(int left, int top, Pix* pix) const; #ifndef GRAPHICS_DISABLED void plot( //draw one ScrollView* window, //window to draw in ScrollView::Color colour) const; //colour to draw it // Draws the outline in the given colour, normalized using the given denorm, // making use of sub-pixel accurate information if available. void plot_normed(const DENORM& denorm, ScrollView::Color colour, ScrollView* window) const; #endif // GRAPHICS_DISABLED C_OUTLINE& operator=(const C_OUTLINE& source); static C_OUTLINE* deep_copy(const C_OUTLINE* src) { C_OUTLINE* outline = new C_OUTLINE; *outline = *src; return outline; } static ICOORD chain_step(int chaindir); // The maximum length of any outline. The stepcount is stored as 16 bits, // but it is probably not a good idea to increase this constant by much // and switch to 32 bits, as it plays an important role in keeping huge // outlines invisible, which prevents bad speed behavior. static const int kMaxOutlineLength = 16000; private: // Helper for ComputeBinaryOffsets. Increments pos, dir_counts, pos_totals // by the step, increment, and vertical step ? x : y position * increment // at step s Mod stepcount respectively. Used to add or subtract the // direction and position to/from accumulators of a small neighbourhood. void increment_step(int s, int increment, ICOORD* pos, int* dir_counts, int* pos_totals) const; int step_mem() const { return (stepcount+3) / 4; } TBOX box; // bounding box ICOORD start; // start coord inT16 stepcount; // no of steps BITS16 flags; // flags about outline uinT8 *steps; // step array EdgeOffset* offsets; // Higher precision edge. C_OUTLINE_LIST children; // child elements static ICOORD step_coords[4]; }; #endif tesseract-3.04.01/ccstruct/crakedge.h000066400000000000000000000025231266071204500174310ustar00rootroot00000000000000/********************************************************************** * File: crakedge.h (Formerly: crkedge.h) * Description: Sturctures for the Crack following edge detector. * Author: Ray Smith * Created: Fri Mar 22 16:06:38 GMT 1991 * * (C) Copyright 1991, 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 CRAKEDGE_H #define CRAKEDGE_H #include "points.h" #include "mod128.h" class CRACKEDGE { public: CRACKEDGE() {} ICOORD pos; /*position of crack */ inT8 stepx; //edge step inT8 stepy; inT8 stepdir; //chaincode CRACKEDGE *prev; /*previous point */ CRACKEDGE *next; /*next point */ }; #endif tesseract-3.04.01/ccstruct/detlinefit.cpp000066400000000000000000000257641266071204500203620ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: detlinefit.cpp // Description: Deterministic least median squares line fitting. // Author: Ray Smith // Created: Thu Feb 28 14:45:01 PDT 2008 // // (C) Copyright 2008, 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 "detlinefit.h" #include "statistc.h" #include "ndminx.h" #include "tprintf.h" namespace tesseract { // The number of points to consider at each end. const int kNumEndPoints = 3; // The minimum number of points at which to switch to number of points // for badly fitted lines. // To ensure a sensible error metric, kMinPointsForErrorCount should be at // least kMaxRealDistance / (1 - %ile) where %ile is the fractile used in // ComputeUpperQuartileError. const int kMinPointsForErrorCount = 16; // The maximum real distance to use before switching to number of // mis-fitted points, which will get square-rooted for true distance. const int kMaxRealDistance = 2.0; DetLineFit::DetLineFit() : square_length_(0.0) { } DetLineFit::~DetLineFit() { } // Delete all Added points. void DetLineFit::Clear() { pts_.clear(); distances_.clear(); } // Add a new point. Takes a copy - the pt doesn't need to stay in scope. void DetLineFit::Add(const ICOORD& pt) { pts_.push_back(PointWidth(pt, 0)); } // Associates a half-width with the given point if a point overlaps the // previous point by more than half the width, and its distance is further // than the previous point, then the more distant point is ignored in the // distance calculation. Useful for ignoring i dots and other diacritics. void DetLineFit::Add(const ICOORD& pt, int halfwidth) { pts_.push_back(PointWidth(pt, halfwidth)); } // Fits a line to the points, ignoring the skip_first initial points and the // skip_last final points, returning the fitted line as a pair of points, // and the upper quartile error. double DetLineFit::Fit(int skip_first, int skip_last, ICOORD* pt1, ICOORD* pt2) { // Do something sensible with no points. if (pts_.empty()) { pt1->set_x(0); pt1->set_y(0); *pt2 = *pt1; return 0.0; } // Count the points and find the first and last kNumEndPoints. int pt_count = pts_.size(); ICOORD* starts[kNumEndPoints]; if (skip_first >= pt_count) skip_first = pt_count - 1; int start_count = 0; int end_i = MIN(skip_first + kNumEndPoints, pt_count); for (int i = skip_first; i < end_i; ++i) { starts[start_count++] = &pts_[i].pt; } ICOORD* ends[kNumEndPoints]; if (skip_last >= pt_count) skip_last = pt_count - 1; int end_count = 0; end_i = MAX(0, pt_count - kNumEndPoints - skip_last); for (int i = pt_count - 1 - skip_last; i >= end_i; --i) { ends[end_count++] = &pts_[i].pt; } // 1 or 2 points need special treatment. if (pt_count <= 2) { *pt1 = *starts[0]; if (pt_count > 1) *pt2 = *ends[0]; else *pt2 = *pt1; return 0.0; } // Although with between 2 and 2*kNumEndPoints-1 points, there will be // overlap in the starts, ends sets, this is OK and taken care of by the // if (*start != *end) test below, which also tests for equal input points. double best_uq = -1.0; // Iterate each pair of points and find the best fitting line. for (int i = 0; i < start_count; ++i) { ICOORD* start = starts[i]; for (int j = 0; j < end_count; ++j) { ICOORD* end = ends[j]; if (*start != *end) { ComputeDistances(*start, *end); // Compute the upper quartile error from the line. double dist = EvaluateLineFit(); if (dist < best_uq || best_uq < 0.0) { best_uq = dist; *pt1 = *start; *pt2 = *end; } } } } // Finally compute the square root to return the true distance. return best_uq > 0.0 ? sqrt(best_uq) : best_uq; } // Constrained fit with a supplied direction vector. Finds the best line_pt, // that is one of the supplied points having the median cross product with // direction, ignoring points that have a cross product outside of the range // [min_dist, max_dist]. Returns the resulting error metric using the same // reduced set of points. // *Makes use of floating point arithmetic* double DetLineFit::ConstrainedFit(const FCOORD& direction, double min_dist, double max_dist, bool debug, ICOORD* line_pt) { ComputeConstrainedDistances(direction, min_dist, max_dist); // Do something sensible with no points or computed distances. if (pts_.empty() || distances_.empty()) { line_pt->set_x(0); line_pt->set_y(0); return 0.0; } int median_index = distances_.choose_nth_item(distances_.size() / 2); *line_pt = distances_[median_index].data; if (debug) { tprintf("Constrained fit to dir %g, %g = %d, %d :%d distances:\n", direction.x(), direction.y(), line_pt->x(), line_pt->y(), distances_.size()); for (int i = 0; i < distances_.size(); ++i) { tprintf("%d: %d, %d -> %g\n", i, distances_[i].data.x(), distances_[i].data.y(), distances_[i].key); } tprintf("Result = %d\n", median_index); } // Center distances on the fitted point. double dist_origin = direction * *line_pt; for (int i = 0; i < distances_.size(); ++i) { distances_[i].key -= dist_origin; } return sqrt(EvaluateLineFit()); } // Returns true if there were enough points at the last call to Fit or // ConstrainedFit for the fitted points to be used on a badly fitted line. bool DetLineFit::SufficientPointsForIndependentFit() const { return distances_.size() >= kMinPointsForErrorCount; } // Backwards compatible fit returning a gradient and constant. // Deprecated. Prefer Fit(ICOORD*, ICOORD*) where possible, but use this // function in preference to the LMS class. double DetLineFit::Fit(float* m, float* c) { ICOORD start, end; double error = Fit(&start, &end); if (end.x() != start.x()) { *m = static_cast(end.y() - start.y()) / (end.x() - start.x()); *c = start.y() - *m * start.x(); } else { *m = 0.0f; *c = 0.0f; } return error; } // Backwards compatible constrained fit with a supplied gradient. // Deprecated. Use ConstrainedFit(const FCOORD& direction) where possible // to avoid potential difficulties with infinite gradients. double DetLineFit::ConstrainedFit(double m, float* c) { // Do something sensible with no points. if (pts_.empty()) { *c = 0.0f; return 0.0; } double cos = 1.0 / sqrt(1.0 + m * m); FCOORD direction(cos, m * cos); ICOORD line_pt; double error = ConstrainedFit(direction, -MAX_FLOAT32, MAX_FLOAT32, false, &line_pt); *c = line_pt.y() - line_pt.x() * m; return error; } // Computes and returns the squared evaluation metric for a line fit. double DetLineFit::EvaluateLineFit() { // Compute the upper quartile error from the line. double dist = ComputeUpperQuartileError(); if (distances_.size() >= kMinPointsForErrorCount && dist > kMaxRealDistance * kMaxRealDistance) { // Use the number of mis-fitted points as the error metric, as this // gives a better measure of fit for badly fitted lines where more // than a quarter are badly fitted. double threshold = kMaxRealDistance * sqrt(square_length_); dist = NumberOfMisfittedPoints(threshold); } return dist; } // Computes the absolute error distances of the points from the line, // and returns the squared upper-quartile error distance. double DetLineFit::ComputeUpperQuartileError() { int num_errors = distances_.size(); if (num_errors == 0) return 0.0; // Get the absolute values of the errors. for (int i = 0; i < num_errors; ++i) { if (distances_[i].key < 0) distances_[i].key = -distances_[i].key; } // Now get the upper quartile distance. int index = distances_.choose_nth_item(3 * num_errors / 4); double dist = distances_[index].key; // The true distance is the square root of the dist squared / square_length. // Don't bother with the square root. Just return the square distance. return square_length_ > 0.0 ? dist * dist / square_length_ : 0.0; } // Returns the number of sample points that have an error more than threshold. int DetLineFit::NumberOfMisfittedPoints(double threshold) const { int num_misfits = 0; int num_dists = distances_.size(); // Get the absolute values of the errors. for (int i = 0; i < num_dists; ++i) { if (distances_[i].key > threshold) ++num_misfits; } return num_misfits; } // Computes all the cross product distances of the points from the line, // storing the actual (signed) cross products in distances. // Ignores distances of points that are further away than the previous point, // and overlaps the previous point by at least half. void DetLineFit::ComputeDistances(const ICOORD& start, const ICOORD& end) { distances_.truncate(0); ICOORD line_vector = end; line_vector -= start; square_length_ = line_vector.sqlength(); int line_length = IntCastRounded(sqrt(square_length_)); // Compute the distance of each point from the line. int prev_abs_dist = 0; int prev_dot = 0; for (int i = 0; i < pts_.size(); ++i) { ICOORD pt_vector = pts_[i].pt; pt_vector -= start; int dot = line_vector % pt_vector; // Compute |line_vector||pt_vector|sin(angle between) int dist = line_vector * pt_vector; int abs_dist = dist < 0 ? -dist : dist; if (abs_dist > prev_abs_dist && i > 0) { // Ignore this point if it overlaps the previous one. int separation = abs(dot - prev_dot); if (separation < line_length * pts_[i].halfwidth || separation < line_length * pts_[i - 1].halfwidth) continue; } distances_.push_back(DistPointPair(dist, pts_[i].pt)); prev_abs_dist = abs_dist; prev_dot = dot; } } // Computes all the cross product distances of the points perpendicular to // the given direction, ignoring distances outside of the give distance range, // storing the actual (signed) cross products in distances_. void DetLineFit::ComputeConstrainedDistances(const FCOORD& direction, double min_dist, double max_dist) { distances_.truncate(0); square_length_ = direction.sqlength(); // Compute the distance of each point from the line. for (int i = 0; i < pts_.size(); ++i) { FCOORD pt_vector = pts_[i].pt; // Compute |line_vector||pt_vector|sin(angle between) double dist = direction * pt_vector; if (min_dist <= dist && dist <= max_dist) distances_.push_back(DistPointPair(dist, pts_[i].pt)); } } } // namespace tesseract. tesseract-3.04.01/ccstruct/detlinefit.h000066400000000000000000000171631266071204500200210ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: detlinefit.h // Description: Deterministic least upper-quartile squares line fitting. // Author: Ray Smith // Created: Thu Feb 28 14:35:01 PDT 2008 // // (C) Copyright 2008, 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_DETLINEFIT_H_ #define TESSERACT_CCSTRUCT_DETLINEFIT_H_ #include "genericvector.h" #include "kdpair.h" #include "points.h" namespace tesseract { // This class fits a line to a set of ICOORD points. // There is no restriction on the direction of the line, as it // uses a vector method, ie no concern over infinite gradients. // The fitted line has the least upper quartile of squares of perpendicular // distances of all source points from the line, subject to the constraint // that the line is made from one of the pairs of [{p1,p2,p3},{pn-2, pn-1, pn}] // i.e. the 9 combinations of one of the first 3 and last 3 points. // A fundamental assumption of this algorithm is that one of the first 3 and // one of the last 3 points are near the best line fit. // The points must be Added in line order for the algorithm to work properly. // No floating point calculations are needed* to make an accurate fit, // and no random numbers are needed** so the algorithm is deterministic, // architecture-stable, and compiler-stable as well as stable to minor // changes in the input. // *A single floating point division is used to compute each line's distance. // This is unlikely to result in choice of a different line, but if it does, // it would be easy to replace with a 64 bit integer calculation. // **Random numbers are used in the nth_item function, but the worst // non-determinism that can result is picking a different result among equals, // and that wouldn't make any difference to the end-result distance, so the // randomness does not affect the determinism of the algorithm. The random // numbers are only there to guarantee average linear time. // Fitting time is linear, but with a high constant, as it tries 9 different // lines and computes the distance of all points each time. // This class is aimed at replacing the LLSQ (linear least squares) and // LMS (least median of squares) classes that are currently used for most // of the line fitting in Tesseract. class DetLineFit { public: DetLineFit(); ~DetLineFit(); // Delete all Added points. void Clear(); // Adds a new point. Takes a copy - the pt doesn't need to stay in scope. // Add must be called on points in sequence along the line. void Add(const ICOORD& pt); // Associates a half-width with the given point if a point overlaps the // previous point by more than half the width, and its distance is further // than the previous point, then the more distant point is ignored in the // distance calculation. Useful for ignoring i dots and other diacritics. void Add(const ICOORD& pt, int halfwidth); // Fits a line to the points, returning the fitted line as a pair of // points, and the upper quartile error. double Fit(ICOORD* pt1, ICOORD* pt2) { return Fit(0, 0, pt1, pt2); } // Fits a line to the points, ignoring the skip_first initial points and the // skip_last final points, returning the fitted line as a pair of points, // and the upper quartile error. double Fit(int skip_first, int skip_last, ICOORD* pt1, ICOORD* pt2); // Constrained fit with a supplied direction vector. Finds the best line_pt, // that is one of the supplied points having the median cross product with // direction, ignoring points that have a cross product outside of the range // [min_dist, max_dist]. Returns the resulting error metric using the same // reduced set of points. // *Makes use of floating point arithmetic* double ConstrainedFit(const FCOORD& direction, double min_dist, double max_dist, bool debug, ICOORD* line_pt); // Returns true if there were enough points at the last call to Fit or // ConstrainedFit for the fitted points to be used on a badly fitted line. bool SufficientPointsForIndependentFit() const; // Backwards compatible fit returning a gradient and constant. // Deprecated. Prefer Fit(ICOORD*, ICOORD*) where possible, but use this // function in preference to the LMS class. double Fit(float* m, float* c); // Backwards compatible constrained fit with a supplied gradient. // Deprecated. Use ConstrainedFit(const FCOORD& direction) where possible // to avoid potential difficulties with infinite gradients. double ConstrainedFit(double m, float* c); private: // Simple struct to hold an ICOORD point and a halfwidth representing half // the "width" (supposedly approximately parallel to the direction of the // line) of each point, such that distant points can be discarded when they // overlap nearer points. (Think i dot and other diacritics or noise.) struct PointWidth { PointWidth() : pt(ICOORD(0, 0)), halfwidth(0) {} PointWidth(const ICOORD& pt0, int halfwidth0) : pt(pt0), halfwidth(halfwidth0) {} ICOORD pt; int halfwidth; }; // Type holds the distance of each point from the fitted line and the point // itself. Use of double allows integer distances from ICOORDs to be stored // exactly, and also the floating point results from ConstrainedFit. typedef KDPairInc DistPointPair; // Computes and returns the squared evaluation metric for a line fit. double EvaluateLineFit(); // Computes the absolute values of the precomputed distances_, // and returns the squared upper-quartile error distance. double ComputeUpperQuartileError(); // Returns the number of sample points that have an error more than threshold. int NumberOfMisfittedPoints(double threshold) const; // Computes all the cross product distances of the points from the line, // storing the actual (signed) cross products in distances_. // Ignores distances of points that are further away than the previous point, // and overlaps the previous point by at least half. void ComputeDistances(const ICOORD& start, const ICOORD& end); // Computes all the cross product distances of the points perpendicular to // the given direction, ignoring distances outside of the give distance range, // storing the actual (signed) cross products in distances_. void ComputeConstrainedDistances(const FCOORD& direction, double min_dist, double max_dist); // Stores all the source points in the order they were given and their // halfwidths, if any. GenericVector pts_; // Stores the computed perpendicular distances of (some of) the pts_ from a // given vector (assuming it goes through the origin, making it a line). // Since the distances may be a subset of the input points, and get // re-ordered by the nth_item function, the original point is stored // along side the distance. GenericVector distances_; // Distances of points. // The squared length of the vector used to compute distances_. double square_length_; }; } // namespace tesseract. #endif // TESSERACT_CCSTRUCT_DETLINEFIT_H_ tesseract-3.04.01/ccstruct/dppoint.cpp000066400000000000000000000070231266071204500176740ustar00rootroot00000000000000/********************************************************************** * File: dppoint.cpp * Description: Simple generic dynamic programming class. * Author: Ray Smith * Created: Wed Mar 25 19:08:01 PDT 2009 * * (C) Copyright 2009, 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 "dppoint.h" #include "tprintf.h" namespace tesseract { // Solve the dynamic programming problem for the given array of points, with // the given size and cost function. // Steps backwards are limited to being between min_step and max_step // inclusive. // The return value is the tail of the best path. DPPoint* DPPoint::Solve(int min_step, int max_step, bool debug, CostFunc cost_func, int size, DPPoint* points) { if (size <= 0 || max_step < min_step || min_step >= size) return NULL; // Degenerate, but not necessarily an error. ASSERT_HOST(min_step > 0); // Infinite loop possible if this is not true. if (debug) tprintf("min = %d, max=%d\n", min_step, max_step); // Evaluate the total cost at each point. for (int i = 0; i < size; ++i) { for (int offset = min_step; offset <= max_step; ++offset) { DPPoint* prev = offset <= i ? points + i - offset : NULL; inT64 new_cost = (points[i].*cost_func)(prev); if (points[i].best_prev_ != NULL && offset > min_step * 2 && new_cost > points[i].total_cost_) break; // Find only the first minimum if going over twice the min. } points[i].total_cost_ += points[i].local_cost_; if (debug) { tprintf("At point %d, local cost=%d, total_cost=%d, steps=%d\n", i, points[i].local_cost_, points[i].total_cost_, points[i].total_steps_); } } // Now find the end of the best path and return it. int best_cost = points[size - 1].total_cost_; int best_end = size - 1; for (int end = best_end - 1; end >= size - min_step; --end) { int cost = points[end].total_cost_; if (cost < best_cost) { best_cost = cost; best_end = end; } } return points + best_end; } // A CostFunc that takes the variance of step into account in the cost. inT64 DPPoint::CostWithVariance(const DPPoint* prev) { if (prev == NULL || prev == this) { UpdateIfBetter(0, 1, NULL, 0, 0, 0); return 0; } int delta = this - prev; inT32 n = prev->n_ + 1; inT32 sig_x = prev->sig_x_ + delta; inT64 sig_xsq = prev->sig_xsq_ + delta * delta; inT64 cost = (sig_xsq - sig_x * sig_x / n) / n; cost += prev->total_cost_; UpdateIfBetter(cost, prev->total_steps_ + 1, prev, n, sig_x, sig_xsq); return cost; } // Update the other members if the cost is lower. void DPPoint::UpdateIfBetter(inT64 cost, inT32 steps, const DPPoint* prev, inT32 n, inT32 sig_x, inT64 sig_xsq) { if (cost < total_cost_) { total_cost_ = cost; total_steps_ = steps; best_prev_ = prev; n_ = n; sig_x_ = sig_x; sig_xsq_ = sig_xsq; } } } // namespace tesseract. tesseract-3.04.01/ccstruct/dppoint.h000066400000000000000000000074341266071204500173470ustar00rootroot00000000000000/********************************************************************** * File: dppoint.h * Description: Simple generic dynamic programming class. * Author: Ray Smith * Created: Wed Mar 25 18:57:01 PDT 2009 * * (C) Copyright 2009, 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_DPPOINT_H__ #define TESSERACT_CCSTRUCT_DPPOINT_H__ #include "host.h" namespace tesseract { // A simple class to provide a dynamic programming solution to a class of // 1st-order problems in which the cost is dependent only on the current // step and the best cost to that step, with a possible special case // of using the variance of the steps, and only the top choice is required. // Useful for problems such as finding the optimal cut points in a fixed-pitch // (vertical or horizontal) situation. // Skeletal Example: // DPPoint* array = new DPPoint[width]; // for (int i = 0; i < width; i++) { // array[i].AddLocalCost(cost_at_i) // } // DPPoint* best_end = DPPoint::Solve(..., array); // while (best_end != NULL) { // int cut_index = best_end - array; // best_end = best_end->best_prev(); // } // delete [] array; class DPPoint { public: // The cost function evaluates the total cost at this (excluding this's // local_cost) and if it beats this's total_cost, then // replace the appropriate values in this. typedef inT64 (DPPoint::*CostFunc)(const DPPoint* prev); DPPoint() : local_cost_(0), total_cost_(MAX_INT32), total_steps_(1), best_prev_(NULL), n_(0), sig_x_(0), sig_xsq_(0) { } // Solve the dynamic programming problem for the given array of points, with // the given size and cost function. // Steps backwards are limited to being between min_step and max_step // inclusive. // The return value is the tail of the best path. static DPPoint* Solve(int min_step, int max_step, bool debug, CostFunc cost_func, int size, DPPoint* points); // A CostFunc that takes the variance of step into account in the cost. inT64 CostWithVariance(const DPPoint* prev); // Accessors. int total_cost() const { return total_cost_; } int Pathlength() const { return total_steps_; } const DPPoint* best_prev() const { return best_prev_; } void AddLocalCost(int new_cost) { local_cost_ += new_cost; } private: // Code common to different cost functions. // Update the other members if the cost is lower. void UpdateIfBetter(inT64 cost, inT32 steps, const DPPoint* prev, inT32 n, inT32 sig_x, inT64 sig_xsq); inT32 local_cost_; // Cost of this point on its own. inT32 total_cost_; // Sum of all costs in best path to here. // During cost calculations local_cost is excluded. inT32 total_steps_; // Number of steps in best path to here. const DPPoint* best_prev_; // Pointer to prev point in best path from here. // Information for computing the variance part of the cost. inT32 n_; // Number of steps in best path to here for variance. inT32 sig_x_; // Sum of step sizes for computing variance. inT64 sig_xsq_; // Sum of squares of steps for computing variance. }; } // namespace tesseract. #endif // TESSERACT_CCSTRUCT_DPPOINT_H__ tesseract-3.04.01/ccstruct/fontinfo.cpp000066400000000000000000000216651266071204500200510ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: fontinfo.cpp // Description: Font information classes abstracted from intproto.h/cpp. // Author: rays@google.com (Ray Smith) // Created: Wed May 18 10:39:01 PDT 2011 // // (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. // /////////////////////////////////////////////////////////////////////// #include "fontinfo.h" #include "bitvector.h" #include "unicity_table.h" namespace tesseract { // Writes to the given file. Returns false in case of error. bool FontInfo::Serialize(FILE* fp) const { if (!write_info(fp, *this)) return false; if (!write_spacing_info(fp, *this)) return false; return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool FontInfo::DeSerialize(bool swap, FILE* fp) { if (!read_info(fp, this, swap)) return false; if (!read_spacing_info(fp, this, swap)) return false; return true; } FontInfoTable::FontInfoTable() { set_compare_callback(NewPermanentTessCallback(CompareFontInfo)); set_clear_callback(NewPermanentTessCallback(FontInfoDeleteCallback)); } FontInfoTable::~FontInfoTable() { } // Writes to the given file. Returns false in case of error. bool FontInfoTable::Serialize(FILE* fp) const { return this->SerializeClasses(fp); } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool FontInfoTable::DeSerialize(bool swap, FILE* fp) { truncate(0); return this->DeSerializeClasses(swap, fp); } // Returns true if the given set of fonts includes one with the same // properties as font_id. bool FontInfoTable::SetContainsFontProperties( int font_id, const GenericVector& font_set) const { uinT32 properties = get(font_id).properties; for (int f = 0; f < font_set.size(); ++f) { if (get(font_set[f].fontinfo_id).properties == properties) return true; } return false; } // Returns true if the given set of fonts includes multiple properties. bool FontInfoTable::SetContainsMultipleFontProperties( const GenericVector& font_set) const { if (font_set.empty()) return false; int first_font = font_set[0].fontinfo_id; uinT32 properties = get(first_font).properties; for (int f = 1; f < font_set.size(); ++f) { if (get(font_set[f].fontinfo_id).properties != properties) return true; } return false; } // Moves any non-empty FontSpacingInfo entries from other to this. void FontInfoTable::MoveSpacingInfoFrom(FontInfoTable* other) { set_compare_callback(NewPermanentTessCallback(CompareFontInfo)); set_clear_callback(NewPermanentTessCallback(FontInfoDeleteCallback)); for (int i = 0; i < other->size(); ++i) { GenericVector* spacing_vec = other->get(i).spacing_vec; if (spacing_vec != NULL) { int target_index = get_index(other->get(i)); if (target_index < 0) { // Bit copy the FontInfo and steal all the pointers. push_back(other->get(i)); other->get(i).name = NULL; } else { delete [] get(target_index).spacing_vec; get(target_index).spacing_vec = other->get(i).spacing_vec; } other->get(i).spacing_vec = NULL; } } } // Moves this to the target unicity table. void FontInfoTable::MoveTo(UnicityTable* target) { target->clear(); target->set_compare_callback(NewPermanentTessCallback(CompareFontInfo)); target->set_clear_callback(NewPermanentTessCallback(FontInfoDeleteCallback)); for (int i = 0; i < size(); ++i) { // Bit copy the FontInfo and steal all the pointers. target->push_back(get(i)); get(i).name = NULL; get(i).spacing_vec = NULL; } } // Compare FontInfo structures. bool CompareFontInfo(const FontInfo& fi1, const FontInfo& fi2) { // The font properties are required to be the same for two font with the same // name, so there is no need to test them. // Consequently, querying the table with only its font name as information is // enough to retrieve its properties. return strcmp(fi1.name, fi2.name) == 0; } // Compare FontSet structures. bool CompareFontSet(const FontSet& fs1, const FontSet& fs2) { if (fs1.size != fs2.size) return false; for (int i = 0; i < fs1.size; ++i) { if (fs1.configs[i] != fs2.configs[i]) return false; } return true; } // Callbacks for GenericVector. void FontInfoDeleteCallback(FontInfo f) { if (f.spacing_vec != NULL) { f.spacing_vec->delete_data_pointers(); delete f.spacing_vec; } delete[] f.name; } void FontSetDeleteCallback(FontSet fs) { delete[] fs.configs; } /*---------------------------------------------------------------------------*/ // Callbacks used by UnicityTable to read/write FontInfo/FontSet structures. bool read_info(FILE* f, FontInfo* fi, bool swap) { inT32 size; if (fread(&size, sizeof(size), 1, f) != 1) return false; if (swap) Reverse32(&size); char* font_name = new char[size + 1]; fi->name = font_name; if (static_cast(fread(font_name, sizeof(*font_name), size, f)) != size) return false; font_name[size] = '\0'; if (fread(&fi->properties, sizeof(fi->properties), 1, f) != 1) return false; if (swap) Reverse32(&fi->properties); return true; } bool write_info(FILE* f, const FontInfo& fi) { inT32 size = strlen(fi.name); if (fwrite(&size, sizeof(size), 1, f) != 1) return false; if (static_cast(fwrite(fi.name, sizeof(*fi.name), size, f)) != size) return false; if (fwrite(&fi.properties, sizeof(fi.properties), 1, f) != 1) return false; return true; } bool read_spacing_info(FILE *f, FontInfo* fi, bool swap) { inT32 vec_size, kern_size; if (fread(&vec_size, sizeof(vec_size), 1, f) != 1) return false; if (swap) Reverse32(&vec_size); ASSERT_HOST(vec_size >= 0); if (vec_size == 0) return true; fi->init_spacing(vec_size); for (int i = 0; i < vec_size; ++i) { FontSpacingInfo *fs = new FontSpacingInfo(); if (fread(&fs->x_gap_before, sizeof(fs->x_gap_before), 1, f) != 1 || fread(&fs->x_gap_after, sizeof(fs->x_gap_after), 1, f) != 1 || fread(&kern_size, sizeof(kern_size), 1, f) != 1) { delete fs; return false; } if (swap) { ReverseN(&(fs->x_gap_before), sizeof(fs->x_gap_before)); ReverseN(&(fs->x_gap_after), sizeof(fs->x_gap_after)); Reverse32(&kern_size); } if (kern_size < 0) { // indication of a NULL entry in fi->spacing_vec delete fs; continue; } if (kern_size > 0 && (!fs->kerned_unichar_ids.DeSerialize(swap, f) || !fs->kerned_x_gaps.DeSerialize(swap, f))) { delete fs; return false; } fi->add_spacing(i, fs); } return true; } bool write_spacing_info(FILE* f, const FontInfo& fi) { inT32 vec_size = (fi.spacing_vec == NULL) ? 0 : fi.spacing_vec->size(); if (fwrite(&vec_size, sizeof(vec_size), 1, f) != 1) return false; inT16 x_gap_invalid = -1; for (int i = 0; i < vec_size; ++i) { FontSpacingInfo *fs = fi.spacing_vec->get(i); inT32 kern_size = (fs == NULL) ? -1 : fs->kerned_x_gaps.size(); if (fs == NULL) { // Valid to have the identical fwrites. Writing invalid x-gaps. if (fwrite(&(x_gap_invalid), sizeof(x_gap_invalid), 1, f) != 1 || fwrite(&(x_gap_invalid), sizeof(x_gap_invalid), 1, f) != 1 || fwrite(&kern_size, sizeof(kern_size), 1, f) != 1) { return false; } } else { if (fwrite(&(fs->x_gap_before), sizeof(fs->x_gap_before), 1, f) != 1 || fwrite(&(fs->x_gap_after), sizeof(fs->x_gap_after), 1, f) != 1 || fwrite(&kern_size, sizeof(kern_size), 1, f) != 1) { return false; } } if (kern_size > 0 && (!fs->kerned_unichar_ids.Serialize(f) || !fs->kerned_x_gaps.Serialize(f))) { return false; } } return true; } bool read_set(FILE* f, FontSet* fs, bool swap) { if (fread(&fs->size, sizeof(fs->size), 1, f) != 1) return false; if (swap) Reverse32(&fs->size); fs->configs = new int[fs->size]; for (int i = 0; i < fs->size; ++i) { if (fread(&fs->configs[i], sizeof(fs->configs[i]), 1, f) != 1) return false; if (swap) Reverse32(&fs->configs[i]); } return true; } bool write_set(FILE* f, const FontSet& fs) { if (fwrite(&fs.size, sizeof(fs.size), 1, f) != 1) return false; for (int i = 0; i < fs.size; ++i) { if (fwrite(&fs.configs[i], sizeof(fs.configs[i]), 1, f) != 1) return false; } return true; } } // namespace tesseract. tesseract-3.04.01/ccstruct/fontinfo.h000066400000000000000000000164061266071204500175130ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: fontinfo.h // Description: Font information classes abstracted from intproto.h/cpp. // Author: rays@google.com (Ray Smith) // Created: Tue May 17 17:08:01 PDT 2011 // // (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_CCSTRUCT_FONTINFO_H_ #define TESSERACT_CCSTRUCT_FONTINFO_H_ #include "genericvector.h" #include "host.h" #include "unichar.h" template class UnicityTable; namespace tesseract { class BitVector; // Simple struct to hold a font and a score. The scores come from the low-level // integer matcher, so they are in the uinT16 range. Fonts are an index to // fontinfo_table. // These get copied around a lot, so best to keep them small. struct ScoredFont { ScoredFont() : fontinfo_id(-1), score(0) {} ScoredFont(int font_id, uinT16 classifier_score) : fontinfo_id(font_id), score(classifier_score) {} // Index into fontinfo table, but inside the classifier, may be a shapetable // index. inT32 fontinfo_id; // Raw score from the low-level classifier. uinT16 score; }; // Struct for information about spacing between characters in a particular font. struct FontSpacingInfo { inT16 x_gap_before; inT16 x_gap_after; GenericVector kerned_unichar_ids; GenericVector kerned_x_gaps; }; /* * font_properties contains properties about boldness, italicness, fixed pitch, * serif, fraktur */ struct FontInfo { FontInfo() : name(NULL), properties(0), universal_id(0), spacing_vec(NULL) {} ~FontInfo() {} // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); // Reserves unicharset_size spots in spacing_vec. void init_spacing(int unicharset_size) { spacing_vec = new GenericVector(); spacing_vec->init_to_size(unicharset_size, NULL); } // Adds the given pointer to FontSpacingInfo to spacing_vec member // (FontInfo class takes ownership of the pointer). // Note: init_spacing should be called before calling this function. void add_spacing(UNICHAR_ID uch_id, FontSpacingInfo *spacing_info) { ASSERT_HOST(spacing_vec != NULL && spacing_vec->size() > uch_id); (*spacing_vec)[uch_id] = spacing_info; } // Returns the pointer to FontSpacingInfo for the given UNICHAR_ID. const FontSpacingInfo *get_spacing(UNICHAR_ID uch_id) const { return (spacing_vec == NULL || spacing_vec->size() <= uch_id) ? NULL : (*spacing_vec)[uch_id]; } // Fills spacing with the value of the x gap expected between the two given // UNICHAR_IDs. Returns true on success. bool get_spacing(UNICHAR_ID prev_uch_id, UNICHAR_ID uch_id, int *spacing) const { const FontSpacingInfo *prev_fsi = this->get_spacing(prev_uch_id); const FontSpacingInfo *fsi = this->get_spacing(uch_id); if (prev_fsi == NULL || fsi == NULL) return false; int i = 0; for (; i < prev_fsi->kerned_unichar_ids.size(); ++i) { if (prev_fsi->kerned_unichar_ids[i] == uch_id) break; } if (i < prev_fsi->kerned_unichar_ids.size()) { *spacing = prev_fsi->kerned_x_gaps[i]; } else { *spacing = prev_fsi->x_gap_after + fsi->x_gap_before; } return true; } bool is_italic() const { return properties & 1; } bool is_bold() const { return (properties & 2) != 0; } bool is_fixed_pitch() const { return (properties & 4) != 0; } bool is_serif() const { return (properties & 8) != 0; } bool is_fraktur() const { return (properties & 16) != 0; } char* name; uinT32 properties; // The universal_id is a field reserved for the initialization process // to assign a unique id number to all fonts loaded for the current // combination of languages. This id will then be returned by // ResultIterator::WordFontAttributes. inT32 universal_id; // Horizontal spacing between characters (indexed by UNICHAR_ID). GenericVector *spacing_vec; }; // Every class (character) owns a FontSet that represents all the fonts that can // render this character. // Since almost all the characters from the same script share the same set of // fonts, the sets are shared over multiple classes (see // Classify::fontset_table_). Thus, a class only store an id to a set. // Because some fonts cannot render just one character of a set, there are a // lot of FontSet that differ only by one font. Rather than storing directly // the FontInfo in the FontSet structure, it's better to share FontInfos among // FontSets (Classify::fontinfo_table_). struct FontSet { int size; int* configs; // FontInfo ids }; // Class that adds a bit of functionality on top of GenericVector to // implement a table of FontInfo that replaces UniCityTable. // TODO(rays) change all references once all existing traineddata files // are replaced. class FontInfoTable : public GenericVector { public: FontInfoTable(); ~FontInfoTable(); // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); // Returns true if the given set of fonts includes one with the same // properties as font_id. bool SetContainsFontProperties( int font_id, const GenericVector& font_set) const; // Returns true if the given set of fonts includes multiple properties. bool SetContainsMultipleFontProperties( const GenericVector& font_set) const; // Moves any non-empty FontSpacingInfo entries from other to this. void MoveSpacingInfoFrom(FontInfoTable* other); // Moves this to the target unicity table. void MoveTo(UnicityTable* target); }; // Compare FontInfo structures. bool CompareFontInfo(const FontInfo& fi1, const FontInfo& fi2); // Compare FontSet structures. bool CompareFontSet(const FontSet& fs1, const FontSet& fs2); // Deletion callbacks for GenericVector. void FontInfoDeleteCallback(FontInfo f); void FontSetDeleteCallback(FontSet fs); // Callbacks used by UnicityTable to read/write FontInfo/FontSet structures. bool read_info(FILE* f, FontInfo* fi, bool swap); bool write_info(FILE* f, const FontInfo& fi); bool read_spacing_info(FILE *f, FontInfo* fi, bool swap); bool write_spacing_info(FILE* f, const FontInfo& fi); bool read_set(FILE* f, FontSet* fs, bool swap); bool write_set(FILE* f, const FontSet& fs); } // namespace tesseract. #endif /* THIRD_PARTY_TESSERACT_CCSTRUCT_FONTINFO_H_ */ tesseract-3.04.01/ccstruct/genblob.cpp000066400000000000000000000031321266071204500176240ustar00rootroot00000000000000/********************************************************************** * File: genblob.cpp (Formerly gblob.c) * Description: Generic Blob processing routines * Author: Phil Cheatle * Created: Mon Nov 25 10:53:26 GMT 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include "genblob.h" #include "stepblob.h" /********************************************************************** * c_blob_comparator() * * Blob comparator used to sort a blob list so that blobs are in increasing * order of left edge. **********************************************************************/ int c_blob_comparator( // sort blobs const void *blob1p, // ptr to ptr to blob1 const void *blob2p // ptr to ptr to blob2 ) { C_BLOB *blob1 = *(C_BLOB **) blob1p; C_BLOB *blob2 = *(C_BLOB **) blob2p; return blob1->bounding_box ().left () - blob2->bounding_box ().left (); } tesseract-3.04.01/ccstruct/genblob.h000066400000000000000000000021741266071204500172760ustar00rootroot00000000000000/********************************************************************** * File: genblob.h (Formerly gblob.h) * Description: Generic Blob processing routines * Author: Phil Cheatle * Created: Mon Nov 25 10:53:26 GMT 1991 * * (C) Copyright 1991, 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 GENBLOB_H #define GENBLOB_H // Sort function to sort blobs by ascending left edge. int c_blob_comparator(const void *blob1p, // ptr to ptr to blob1 const void *blob2p); #endif tesseract-3.04.01/ccstruct/hpdsizes.h000066400000000000000000000002521266071204500175120ustar00rootroot00000000000000#ifndef HPDSIZES_H #define HPDSIZES_H #define NUM_TEXT_ATTR 10 #define NUM_BLOCK_ATTR 7 #define MAXLENGTH 128 #define NUM_BACKGROUNDS 8 #endif tesseract-3.04.01/ccstruct/imagedata.cpp000066400000000000000000000431371266071204500201410ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: imagedata.h // Description: Class to hold information about a single multi-page tiff // training file and its corresponding boxes or text file. // Author: Ray Smith // Created: Tue May 28 08:56:06 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 automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "imagedata.h" #include "allheaders.h" #include "boxread.h" #include "callcpp.h" #include "helpers.h" #include "tprintf.h" namespace tesseract { WordFeature::WordFeature() : x_(0), y_(0), dir_(0) { } WordFeature::WordFeature(const FCOORD& fcoord, uinT8 dir) : x_(IntCastRounded(fcoord.x())), y_(ClipToRange(IntCastRounded(fcoord.y()), 0, MAX_UINT8)), dir_(dir) { } // Computes the maximum x and y value in the features. void WordFeature::ComputeSize(const GenericVector& features, int* max_x, int* max_y) { *max_x = 0; *max_y = 0; for (int f = 0; f < features.size(); ++f) { if (features[f].x_ > *max_x) *max_x = features[f].x_; if (features[f].y_ > *max_y) *max_y = features[f].y_; } } // Draws the features in the given window. void WordFeature::Draw(const GenericVector& features, ScrollView* window) { #ifndef GRAPHICS_DISABLED for (int f = 0; f < features.size(); ++f) { FCOORD pos(features[f].x_, features[f].y_); FCOORD dir; dir.from_direction(features[f].dir_); dir *= 8.0f; window->SetCursor(IntCastRounded(pos.x() - dir.x()), IntCastRounded(pos.y() - dir.y())); window->DrawTo(IntCastRounded(pos.x() + dir.x()), IntCastRounded(pos.y() + dir.y())); } #endif } // Writes to the given file. Returns false in case of error. bool WordFeature::Serialize(FILE* fp) const { if (fwrite(&x_, sizeof(x_), 1, fp) != 1) return false; if (fwrite(&y_, sizeof(y_), 1, fp) != 1) return false; if (fwrite(&dir_, sizeof(dir_), 1, fp) != 1) return false; return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool WordFeature::DeSerialize(bool swap, FILE* fp) { if (fread(&x_, sizeof(x_), 1, fp) != 1) return false; if (swap) ReverseN(&x_, sizeof(x_)); if (fread(&y_, sizeof(y_), 1, fp) != 1) return false; if (fread(&dir_, sizeof(dir_), 1, fp) != 1) return false; return true; } void FloatWordFeature::FromWordFeatures( const GenericVector& word_features, GenericVector* float_features) { for (int i = 0; i < word_features.size(); ++i) { FloatWordFeature f; f.x = word_features[i].x(); f.y = word_features[i].y(); f.dir = word_features[i].dir(); f.x_bucket = 0; // Will set it later. float_features->push_back(f); } } // Sort function to sort first by x-bucket, then by y. /* static */ int FloatWordFeature::SortByXBucket(const void* v1, const void* v2) { const FloatWordFeature* f1 = reinterpret_cast(v1); const FloatWordFeature* f2 = reinterpret_cast(v2); int x_diff = f1->x_bucket - f2->x_bucket; if (x_diff == 0) return f1->y - f2->y; return x_diff; } ImageData::ImageData() : page_number_(-1), vertical_text_(false) { } // Takes ownership of the pix and destroys it. ImageData::ImageData(bool vertical, Pix* pix) : page_number_(0), vertical_text_(vertical) { SetPix(pix); } ImageData::~ImageData() { } // Builds and returns an ImageData from the basic data. Note that imagedata, // truth_text, and box_text are all the actual file data, NOT filenames. ImageData* ImageData::Build(const char* name, int page_number, const char* lang, const char* imagedata, int imagedatasize, const char* truth_text, const char* box_text) { ImageData* image_data = new ImageData(); image_data->imagefilename_ = name; image_data->page_number_ = page_number; image_data->language_ = lang; // Save the imagedata. image_data->image_data_.init_to_size(imagedatasize, 0); memcpy(&image_data->image_data_[0], imagedata, imagedatasize); if (!image_data->AddBoxes(box_text)) { if (truth_text == NULL || truth_text[0] == '\0') { tprintf("Error: No text corresponding to page %d from image %s!\n", page_number, name); delete image_data; return NULL; } image_data->transcription_ = truth_text; // If we have no boxes, the transcription is in the 0th box_texts_. image_data->box_texts_.push_back(truth_text); // We will create a box for the whole image on PreScale, to save unpacking // the image now. } else if (truth_text != NULL && truth_text[0] != '\0' && image_data->transcription_ != truth_text) { // Save the truth text as it is present and disagrees with the box text. image_data->transcription_ = truth_text; } return image_data; } // Writes to the given file. Returns false in case of error. bool ImageData::Serialize(TFile* fp) const { if (!imagefilename_.Serialize(fp)) return false; if (fp->FWrite(&page_number_, sizeof(page_number_), 1) != 1) return false; if (!image_data_.Serialize(fp)) return false; if (!transcription_.Serialize(fp)) return false; // WARNING: Will not work across different endian machines. if (!boxes_.Serialize(fp)) return false; if (!box_texts_.SerializeClasses(fp)) return false; inT8 vertical = vertical_text_; if (fp->FWrite(&vertical, sizeof(vertical), 1) != 1) return false; return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool ImageData::DeSerialize(bool swap, TFile* fp) { if (!imagefilename_.DeSerialize(swap, fp)) return false; if (fp->FRead(&page_number_, sizeof(page_number_), 1) != 1) return false; if (swap) ReverseN(&page_number_, sizeof(page_number_)); if (!image_data_.DeSerialize(swap, fp)) return false; if (!transcription_.DeSerialize(swap, fp)) return false; // WARNING: Will not work across different endian machines. if (!boxes_.DeSerialize(swap, fp)) return false; if (!box_texts_.DeSerializeClasses(swap, fp)) return false; inT8 vertical = 0; if (fp->FRead(&vertical, sizeof(vertical), 1) != 1) return false; vertical_text_ = vertical != 0; return true; } // Saves the given Pix as a PNG-encoded string and destroys it. void ImageData::SetPix(Pix* pix) { SetPixInternal(pix, &image_data_); } // Returns the Pix image for *this. Must be pixDestroyed after use. Pix* ImageData::GetPix() const { return GetPixInternal(image_data_); } // Gets anything and everything with a non-NULL pointer, prescaled to a // given target_height (if 0, then the original image height), and aligned. // Also returns (if not NULL) the width and height of the scaled image. // The return value is the scale factor that was applied to the image to // achieve the target_height. float ImageData::PreScale(int target_height, Pix** pix, int* scaled_width, int* scaled_height, GenericVector* boxes) const { int input_width = 0; int input_height = 0; Pix* src_pix = GetPix(); ASSERT_HOST(src_pix != NULL); input_width = pixGetWidth(src_pix); input_height = pixGetHeight(src_pix); if (target_height == 0) target_height = input_height; float im_factor = static_cast(target_height) / input_height; if (scaled_width != NULL) *scaled_width = IntCastRounded(im_factor * input_width); if (scaled_height != NULL) *scaled_height = target_height; if (pix != NULL) { // Get the scaled image. pixDestroy(pix); *pix = pixScale(src_pix, im_factor, im_factor); if (*pix == NULL) { tprintf("Scaling pix of size %d, %d by factor %g made null pix!!\n", input_width, input_height, im_factor); } if (scaled_width != NULL) *scaled_width = pixGetWidth(*pix); if (scaled_height != NULL) *scaled_height = pixGetHeight(*pix); } pixDestroy(&src_pix); if (boxes != NULL) { // Get the boxes. boxes->truncate(0); for (int b = 0; b < boxes_.size(); ++b) { TBOX box = boxes_[b]; box.scale(im_factor); boxes->push_back(box); } if (boxes->empty()) { // Make a single box for the whole image. TBOX box(0, 0, im_factor * input_width, target_height); boxes->push_back(box); } } return im_factor; } int ImageData::MemoryUsed() const { return image_data_.size(); } // Draws the data in a new window. void ImageData::Display() const { #ifndef GRAPHICS_DISABLED const int kTextSize = 64; // Draw the image. Pix* pix = GetPix(); if (pix == NULL) return; int width = pixGetWidth(pix); int height = pixGetHeight(pix); ScrollView* win = new ScrollView("Imagedata", 100, 100, 2 * (width + 2 * kTextSize), 2 * (height + 4 * kTextSize), width + 10, height + 3 * kTextSize, true); win->Image(pix, 0, height - 1); pixDestroy(&pix); // Draw the boxes. win->Pen(ScrollView::RED); win->Brush(ScrollView::NONE); win->TextAttributes("Arial", kTextSize, false, false, false); for (int b = 0; b < boxes_.size(); ++b) { boxes_[b].plot(win); win->Text(boxes_[b].left(), height + kTextSize, box_texts_[b].string()); TBOX scaled(boxes_[b]); scaled.scale(256.0 / height); scaled.plot(win); } // The full transcription. win->Pen(ScrollView::CYAN); win->Text(0, height + kTextSize * 2, transcription_.string()); // Add the features. win->Pen(ScrollView::GREEN); win->Update(); window_wait(win); #endif } // Adds the supplied boxes and transcriptions that correspond to the correct // page number. void ImageData::AddBoxes(const GenericVector& boxes, const GenericVector& texts, const GenericVector& box_pages) { // Copy the boxes and make the transcription. for (int i = 0; i < box_pages.size(); ++i) { if (page_number_ >= 0 && box_pages[i] != page_number_) continue; transcription_ += texts[i]; boxes_.push_back(boxes[i]); box_texts_.push_back(texts[i]); } } // Saves the given Pix as a PNG-encoded string and destroys it. void ImageData::SetPixInternal(Pix* pix, GenericVector* image_data) { l_uint8* data; size_t size; pixWriteMem(&data, &size, pix, IFF_PNG); pixDestroy(&pix); image_data->init_to_size(size, 0); memcpy(&(*image_data)[0], data, size); free(data); } // Returns the Pix image for the image_data. Must be pixDestroyed after use. Pix* ImageData::GetPixInternal(const GenericVector& image_data) { Pix* pix = NULL; if (!image_data.empty()) { // Convert the array to an image. const unsigned char* u_data = reinterpret_cast(&image_data[0]); pix = pixReadMem(u_data, image_data.size()); } return pix; } // Parses the text string as a box file and adds any discovered boxes that // match the page number. Returns false on error. bool ImageData::AddBoxes(const char* box_text) { if (box_text != NULL && box_text[0] != '\0') { GenericVector boxes; GenericVector texts; GenericVector box_pages; if (ReadMemBoxes(page_number_, false, box_text, &boxes, &texts, NULL, &box_pages)) { AddBoxes(boxes, texts, box_pages); return true; } else { tprintf("Error: No boxes for page %d from image %s!\n", page_number_, imagefilename_.string()); } } return false; } DocumentData::DocumentData(const STRING& name) : document_name_(name), pages_offset_(0), total_pages_(0), memory_used_(0), max_memory_(0), reader_(NULL) {} DocumentData::~DocumentData() {} // Reads all the pages in the given lstmf filename to the cache. The reader // is used to read the file. bool DocumentData::LoadDocument(const char* filename, const char* lang, int start_page, inT64 max_memory, FileReader reader) { document_name_ = filename; lang_ = lang; pages_offset_ = start_page; max_memory_ = max_memory; reader_ = reader; return ReCachePages(); } // Writes all the pages to the given filename. Returns false on error. bool DocumentData::SaveDocument(const char* filename, FileWriter writer) { TFile fp; fp.OpenWrite(NULL); if (!pages_.Serialize(&fp) || !fp.CloseWrite(filename, writer)) { tprintf("Serialize failed: %s\n", filename); return false; } return true; } bool DocumentData::SaveToBuffer(GenericVector* buffer) { TFile fp; fp.OpenWrite(buffer); return pages_.Serialize(&fp); } // Returns a pointer to the page with the given index, modulo the total // number of pages, recaching if needed. const ImageData* DocumentData::GetPage(int index) { index = Modulo(index, total_pages_); if (index < pages_offset_ || index >= pages_offset_ + pages_.size()) { pages_offset_ = index; if (!ReCachePages()) return NULL; } return pages_[index - pages_offset_]; } // Loads as many pages can fit in max_memory_ starting at index pages_offset_. bool DocumentData::ReCachePages() { // Read the file. TFile fp; if (!fp.Open(document_name_, reader_)) return false; memory_used_ = 0; if (!pages_.DeSerialize(false, &fp)) { tprintf("Deserialize failed: %s\n", document_name_.string()); pages_.truncate(0); return false; } total_pages_ = pages_.size(); pages_offset_ %= total_pages_; // Delete pages before the first one we want, and relocate the rest. int page; for (page = 0; page < pages_.size(); ++page) { if (page < pages_offset_) { delete pages_[page]; pages_[page] = NULL; } else { ImageData* image_data = pages_[page]; if (max_memory_ > 0 && page > pages_offset_ && memory_used_ + image_data->MemoryUsed() > max_memory_) break; // Don't go over memory quota unless the first image. if (image_data->imagefilename().length() == 0) { image_data->set_imagefilename(document_name_); image_data->set_page_number(page); } image_data->set_language(lang_); memory_used_ += image_data->MemoryUsed(); if (pages_offset_ != 0) { pages_[page - pages_offset_] = image_data; pages_[page] = NULL; } } } pages_.truncate(page - pages_offset_); tprintf("Loaded %d/%d pages (%d-%d) of document %s\n", pages_.size(), total_pages_, pages_offset_, pages_offset_ + pages_.size(), document_name_.string()); return !pages_.empty(); } // Adds the given page data to this document, counting up memory. void DocumentData::AddPageToDocument(ImageData* page) { pages_.push_back(page); memory_used_ += page->MemoryUsed(); } // A collection of DocumentData that knows roughly how much memory it is using. DocumentCache::DocumentCache(inT64 max_memory) : total_pages_(0), memory_used_(0), max_memory_(max_memory) {} DocumentCache::~DocumentCache() {} // Adds all the documents in the list of filenames, counting memory. // The reader is used to read the files. bool DocumentCache::LoadDocuments(const GenericVector& filenames, const char* lang, FileReader reader) { inT64 fair_share_memory = max_memory_ / filenames.size(); for (int arg = 0; arg < filenames.size(); ++arg) { STRING filename = filenames[arg]; DocumentData* document = new DocumentData(filename); if (document->LoadDocument(filename.string(), lang, 0, fair_share_memory, reader)) { AddToCache(document); } else { tprintf("Failed to load image %s!\n", filename.string()); delete document; } } tprintf("Loaded %d pages, total %gMB\n", total_pages_, memory_used_ / 1048576.0); return total_pages_ > 0; } // Adds document to the cache, throwing out other documents if needed. bool DocumentCache::AddToCache(DocumentData* data) { inT64 new_memory = data->memory_used(); memory_used_ += new_memory; documents_.push_back(data); total_pages_ += data->NumPages(); // Delete the first item in the array, and other pages of the same name // while memory is full. while (memory_used_ >= max_memory_ && max_memory_ > 0) { tprintf("Memory used=%lld vs max=%lld, discarding doc of size %lld\n", memory_used_ , max_memory_, documents_[0]->memory_used()); memory_used_ -= documents_[0]->memory_used(); total_pages_ -= documents_[0]->NumPages(); documents_.remove(0); } return true; } // Finds and returns a document by name. DocumentData* DocumentCache::FindDocument(const STRING& document_name) const { for (int i = 0; i < documents_.size(); ++i) { if (documents_[i]->document_name() == document_name) return documents_[i]; } return NULL; } // Returns a page by serial number, selecting them in a round-robin fashion // from all the documents. const ImageData* DocumentCache::GetPageBySerial(int serial) { int document_index = serial % documents_.size(); return documents_[document_index]->GetPage(serial / documents_.size()); } } // namespace tesseract. tesseract-3.04.01/ccstruct/imagedata.h000066400000000000000000000232521266071204500176020ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: imagedata.h // Description: Class to hold information about a single image and its // corresponding boxes or text file. // Author: Ray Smith // Created: Mon Jul 22 14:17:06 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. /////////////////////////////////////////////////////////////////////// #ifndef TESSERACT_IMAGE_IMAGEDATA_H_ #define TESSERACT_IMAGE_IMAGEDATA_H_ #include "genericvector.h" #include "normalis.h" #include "rect.h" #include "strngs.h" struct Pix; namespace tesseract { // Amount of padding to apply in output pixels in feature mode. const int kFeaturePadding = 2; // Number of pixels to pad around text boxes. const int kImagePadding = 4; // Number of training images to combine into a mini-batch for training. const int kNumPagesPerMiniBatch = 100; class WordFeature { public: WordFeature(); WordFeature(const FCOORD& fcoord, uinT8 dir); // Computes the maximum x and y value in the features. static void ComputeSize(const GenericVector& features, int* max_x, int* max_y); // Draws the features in the given window. static void Draw(const GenericVector& features, ScrollView* window); // Accessors. int x() const { return x_; } int y() const { return y_; } int dir() const { return dir_; } // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); private: inT16 x_; uinT8 y_; uinT8 dir_; }; // A floating-point version of WordFeature, used as an intermediate during // scaling. struct FloatWordFeature { static void FromWordFeatures(const GenericVector& word_features, GenericVector* float_features); // Sort function to sort first by x-bucket, then by y. static int SortByXBucket(const void*, const void*); float x; float y; float dir; int x_bucket; }; // Class to hold information on a single image: // Filename, cached image as a Pix*, character boxes, text transcription. // The text transcription is the ground truth UTF-8 text for the image. // Character boxes are optional and indicate the desired segmentation of // the text into recognition units. class ImageData { public: ImageData(); // Takes ownership of the pix. ImageData(bool vertical, Pix* pix); ~ImageData(); // Builds and returns an ImageData from the basic data. Note that imagedata, // truth_text, and box_text are all the actual file data, NOT filenames. static ImageData* Build(const char* name, int page_number, const char* lang, const char* imagedata, int imagedatasize, const char* truth_text, const char* box_text); // Writes to the given file. Returns false in case of error. bool Serialize(TFile* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, TFile* fp); // Other accessors. const STRING& imagefilename() const { return imagefilename_; } void set_imagefilename(const STRING& name) { imagefilename_ = name; } int page_number() const { return page_number_; } void set_page_number(int num) { page_number_ = num; } const GenericVector& image_data() const { return image_data_; } const STRING& language() const { return language_; } void set_language(const STRING& lang) { language_ = lang; } const STRING& transcription() const { return transcription_; } const GenericVector& boxes() const { return boxes_; } const GenericVector& box_texts() const { return box_texts_; } const STRING& box_text(int index) const { return box_texts_[index]; } // Saves the given Pix as a PNG-encoded string and destroys it. void SetPix(Pix* pix); // Returns the Pix image for *this. Must be pixDestroyed after use. Pix* GetPix() const; // Gets anything and everything with a non-NULL pointer, prescaled to a // given target_height (if 0, then the original image height), and aligned. // Also returns (if not NULL) the width and height of the scaled image. // The return value is the scale factor that was applied to the image to // achieve the target_height. float PreScale(int target_height, Pix** pix, int* scaled_width, int* scaled_height, GenericVector* boxes) const; int MemoryUsed() const; // Draws the data in a new window. void Display() const; // Adds the supplied boxes and transcriptions that correspond to the correct // page number. void AddBoxes(const GenericVector& boxes, const GenericVector& texts, const GenericVector& box_pages); private: // Saves the given Pix as a PNG-encoded string and destroys it. static void SetPixInternal(Pix* pix, GenericVector* image_data); // Returns the Pix image for the image_data. Must be pixDestroyed after use. static Pix* GetPixInternal(const GenericVector& image_data); // Parses the text string as a box file and adds any discovered boxes that // match the page number. Returns false on error. bool AddBoxes(const char* box_text); private: STRING imagefilename_; // File to read image from. inT32 page_number_; // Page number if multi-page tif or -1. GenericVector image_data_; // PNG file data. STRING language_; // Language code for image. STRING transcription_; // UTF-8 ground truth of image. GenericVector boxes_; // If non-empty boxes of the image. GenericVector box_texts_; // String for text in each box. bool vertical_text_; // Image has been rotated from vertical. }; // A collection of ImageData that knows roughly how much memory it is using. class DocumentData { public: explicit DocumentData(const STRING& name); ~DocumentData(); // Reads all the pages in the given lstmf filename to the cache. The reader // is used to read the file. bool LoadDocument(const char* filename, const char* lang, int start_page, inT64 max_memory, FileReader reader); // Writes all the pages to the given filename. Returns false on error. bool SaveDocument(const char* filename, FileWriter writer); bool SaveToBuffer(GenericVector* buffer); // Adds the given page data to this document, counting up memory. void AddPageToDocument(ImageData* page); const STRING& document_name() const { return document_name_; } int NumPages() const { return total_pages_; } inT64 memory_used() const { return memory_used_; } // Returns a pointer to the page with the given index, modulo the total // number of pages, recaching if needed. const ImageData* GetPage(int index); // Takes ownership of the given page index. The page is made NULL in *this. ImageData* TakePage(int index) { ImageData* page = pages_[index]; pages_[index] = NULL; return page; } private: // Loads as many pages can fit in max_memory_ starting at index pages_offset_. bool ReCachePages(); private: // A name for this document. STRING document_name_; // The language of this document. STRING lang_; // A group of pages that corresponds in some loose way to a document. PointerVector pages_; // Page number of the first index in pages_. int pages_offset_; // Total number of pages in document (may exceed size of pages_.) int total_pages_; // Total of all pix sizes in the document. inT64 memory_used_; // Max memory to use at any time. inT64 max_memory_; // Saved reader from LoadDocument to allow re-caching. FileReader reader_; }; // A collection of DocumentData that knows roughly how much memory it is using. class DocumentCache { public: explicit DocumentCache(inT64 max_memory); ~DocumentCache(); // Adds all the documents in the list of filenames, counting memory. // The reader is used to read the files. bool LoadDocuments(const GenericVector& filenames, const char* lang, FileReader reader); // Adds document to the cache, throwing out other documents if needed. bool AddToCache(DocumentData* data); // Finds and returns a document by name. DocumentData* FindDocument(const STRING& document_name) const; // Returns a page by serial number, selecting them in a round-robin fashion // from all the documents. const ImageData* GetPageBySerial(int serial); const PointerVector& documents() const { return documents_; } int total_pages() const { return total_pages_; } private: // A group of pages that corresponds in some loose way to a document. PointerVector documents_; // Total of all pages. int total_pages_; // Total of all memory used by the cache. inT64 memory_used_; // Max memory allowed in this cache. inT64 max_memory_; }; } // namespace tesseract #endif // TESSERACT_IMAGE_IMAGEDATA_H_ tesseract-3.04.01/ccstruct/ipoints.h000066400000000000000000000311751266071204500173560ustar00rootroot00000000000000/********************************************************************** * File: ipoints.h (Formerly icoords.h) * Description: Inline functions for coords.h. * Author: Ray Smith * Created: Fri Jun 21 15:14:21 BST 1991 * * (C) Copyright 1991, 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 IPOINTS_H #define IPOINTS_H #include /********************************************************************** * operator! * * Rotate an ICOORD 90 degrees anticlockwise. **********************************************************************/ inline ICOORD operator! ( //rotate 90 deg anti const ICOORD & src //thing to rotate ) { ICOORD result; //output result.xcoord = -src.ycoord; result.ycoord = src.xcoord; return result; } /********************************************************************** * operator- * * Unary minus of an ICOORD. **********************************************************************/ inline ICOORD operator- ( //unary minus const ICOORD & src //thing to minus ) { ICOORD result; //output result.xcoord = -src.xcoord; result.ycoord = -src.ycoord; return result; } /********************************************************************** * operator+ * * Add 2 ICOORDS. **********************************************************************/ inline ICOORD operator+ ( //sum vectors const ICOORD & op1, //operands const ICOORD & op2) { ICOORD sum; //result sum.xcoord = op1.xcoord + op2.xcoord; sum.ycoord = op1.ycoord + op2.ycoord; return sum; } /********************************************************************** * operator+= * * Add 2 ICOORDS. **********************************************************************/ inline ICOORD & operator+= ( //sum vectors ICOORD & op1, //operands const ICOORD & op2) { op1.xcoord += op2.xcoord; op1.ycoord += op2.ycoord; return op1; } /********************************************************************** * operator- * * Subtract 2 ICOORDS. **********************************************************************/ inline ICOORD operator- ( //subtract vectors const ICOORD & op1, //operands const ICOORD & op2) { ICOORD sum; //result sum.xcoord = op1.xcoord - op2.xcoord; sum.ycoord = op1.ycoord - op2.ycoord; return sum; } /********************************************************************** * operator-= * * Subtract 2 ICOORDS. **********************************************************************/ inline ICOORD & operator-= ( //sum vectors ICOORD & op1, //operands const ICOORD & op2) { op1.xcoord -= op2.xcoord; op1.ycoord -= op2.ycoord; return op1; } /********************************************************************** * operator% * * Scalar product of 2 ICOORDS. **********************************************************************/ inline inT32 operator% ( //scalar product const ICOORD & op1, //operands const ICOORD & op2) { return op1.xcoord * op2.xcoord + op1.ycoord * op2.ycoord; } /********************************************************************** * operator* * * Cross product of 2 ICOORDS. **********************************************************************/ inline inT32 operator *( //cross product const ICOORD &op1, //operands const ICOORD &op2) { return op1.xcoord * op2.ycoord - op1.ycoord * op2.xcoord; } /********************************************************************** * operator* * * Scalar multiply of an ICOORD. **********************************************************************/ inline ICOORD operator *( //scalar multiply const ICOORD &op1, //operands inT16 scale) { ICOORD result; //output result.xcoord = op1.xcoord * scale; result.ycoord = op1.ycoord * scale; return result; } inline ICOORD operator *( //scalar multiply inT16 scale, const ICOORD &op1 //operands ) { ICOORD result; //output result.xcoord = op1.xcoord * scale; result.ycoord = op1.ycoord * scale; return result; } /********************************************************************** * operator*= * * Scalar multiply of an ICOORD. **********************************************************************/ inline ICOORD & operator*= ( //scalar multiply ICOORD & op1, //operands inT16 scale) { op1.xcoord *= scale; op1.ycoord *= scale; return op1; } /********************************************************************** * operator/ * * Scalar divide of an ICOORD. **********************************************************************/ inline ICOORD operator/ ( //scalar divide const ICOORD & op1, //operands inT16 scale) { ICOORD result; //output result.xcoord = op1.xcoord / scale; result.ycoord = op1.ycoord / scale; return result; } /********************************************************************** * operator/= * * Scalar divide of an ICOORD. **********************************************************************/ inline ICOORD & operator/= ( //scalar divide ICOORD & op1, //operands inT16 scale) { op1.xcoord /= scale; op1.ycoord /= scale; return op1; } /********************************************************************** * ICOORD::rotate * * Rotate an ICOORD by the given (normalized) (cos,sin) vector. **********************************************************************/ inline void ICOORD::rotate( //rotate by vector const FCOORD& vec) { inT16 tmp; tmp = (inT16) floor (xcoord * vec.x () - ycoord * vec.y () + 0.5); ycoord = (inT16) floor (ycoord * vec.x () + xcoord * vec.y () + 0.5); xcoord = tmp; } /********************************************************************** * operator! * * Rotate an FCOORD 90 degrees anticlockwise. **********************************************************************/ inline FCOORD operator! ( //rotate 90 deg anti const FCOORD & src //thing to rotate ) { FCOORD result; //output result.xcoord = -src.ycoord; result.ycoord = src.xcoord; return result; } /********************************************************************** * operator- * * Unary minus of an FCOORD. **********************************************************************/ inline FCOORD operator- ( //unary minus const FCOORD & src //thing to minus ) { FCOORD result; //output result.xcoord = -src.xcoord; result.ycoord = -src.ycoord; return result; } /********************************************************************** * operator+ * * Add 2 FCOORDS. **********************************************************************/ inline FCOORD operator+ ( //sum vectors const FCOORD & op1, //operands const FCOORD & op2) { FCOORD sum; //result sum.xcoord = op1.xcoord + op2.xcoord; sum.ycoord = op1.ycoord + op2.ycoord; return sum; } /********************************************************************** * operator+= * * Add 2 FCOORDS. **********************************************************************/ inline FCOORD & operator+= ( //sum vectors FCOORD & op1, //operands const FCOORD & op2) { op1.xcoord += op2.xcoord; op1.ycoord += op2.ycoord; return op1; } /********************************************************************** * operator- * * Subtract 2 FCOORDS. **********************************************************************/ inline FCOORD operator- ( //subtract vectors const FCOORD & op1, //operands const FCOORD & op2) { FCOORD sum; //result sum.xcoord = op1.xcoord - op2.xcoord; sum.ycoord = op1.ycoord - op2.ycoord; return sum; } /********************************************************************** * operator-= * * Subtract 2 FCOORDS. **********************************************************************/ inline FCOORD & operator-= ( //sum vectors FCOORD & op1, //operands const FCOORD & op2) { op1.xcoord -= op2.xcoord; op1.ycoord -= op2.ycoord; return op1; } /********************************************************************** * operator% * * Scalar product of 2 FCOORDS. **********************************************************************/ inline float operator% ( //scalar product const FCOORD & op1, //operands const FCOORD & op2) { return op1.xcoord * op2.xcoord + op1.ycoord * op2.ycoord; } /********************************************************************** * operator* * * Cross product of 2 FCOORDS. **********************************************************************/ inline float operator *( //cross product const FCOORD &op1, //operands const FCOORD &op2) { return op1.xcoord * op2.ycoord - op1.ycoord * op2.xcoord; } /********************************************************************** * operator* * * Scalar multiply of an FCOORD. **********************************************************************/ inline FCOORD operator *( //scalar multiply const FCOORD &op1, //operands float scale) { FCOORD result; //output result.xcoord = op1.xcoord * scale; result.ycoord = op1.ycoord * scale; return result; } inline FCOORD operator *( //scalar multiply float scale, const FCOORD &op1 //operands ) { FCOORD result; //output result.xcoord = op1.xcoord * scale; result.ycoord = op1.ycoord * scale; return result; } /********************************************************************** * operator*= * * Scalar multiply of an FCOORD. **********************************************************************/ inline FCOORD & operator*= ( //scalar multiply FCOORD & op1, //operands float scale) { op1.xcoord *= scale; op1.ycoord *= scale; return op1; } /********************************************************************** * operator/ * * Scalar divide of an FCOORD. **********************************************************************/ inline FCOORD operator/ ( //scalar divide const FCOORD & op1, //operands float scale) { FCOORD result; //output if (scale != 0) { result.xcoord = op1.xcoord / scale; result.ycoord = op1.ycoord / scale; } return result; } /********************************************************************** * operator/= * * Scalar divide of an FCOORD. **********************************************************************/ inline FCOORD & operator/= ( //scalar divide FCOORD & op1, //operands float scale) { if (scale != 0) { op1.xcoord /= scale; op1.ycoord /= scale; } return op1; } /********************************************************************** * rotate * * Rotate an FCOORD by the given (normalized) (cos,sin) vector. **********************************************************************/ inline void FCOORD::rotate( //rotate by vector const FCOORD vec) { float tmp; tmp = xcoord * vec.x () - ycoord * vec.y (); ycoord = ycoord * vec.x () + xcoord * vec.y (); xcoord = tmp; } inline void FCOORD::unrotate(const FCOORD& vec) { rotate(FCOORD(vec.x(), -vec.y())); } #endif tesseract-3.04.01/ccstruct/linlsq.cpp000066400000000000000000000227071266071204500175270ustar00rootroot00000000000000/********************************************************************** * File: linlsq.cpp (Formerly llsq.c) * Description: Linear Least squares fitting code. * Author: Ray Smith * Created: Thu Sep 12 08:44:51 BST 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include #include #include "errcode.h" #include "linlsq.h" const ERRCODE EMPTY_LLSQ = "Can't delete from an empty LLSQ"; /********************************************************************** * LLSQ::clear * * Function to initialize a LLSQ. **********************************************************************/ void LLSQ::clear() { // initialize total_weight = 0.0; // no elements sigx = 0.0; // update accumulators sigy = 0.0; sigxx = 0.0; sigxy = 0.0; sigyy = 0.0; } /********************************************************************** * LLSQ::add * * Add an element to the accumulator. **********************************************************************/ void LLSQ::add(double x, double y) { // add an element total_weight++; // count elements sigx += x; // update accumulators sigy += y; sigxx += x * x; sigxy += x * y; sigyy += y * y; } // Adds an element with a specified weight. void LLSQ::add(double x, double y, double weight) { total_weight += weight; sigx += x * weight; // update accumulators sigy += y * weight; sigxx += x * x * weight; sigxy += x * y * weight; sigyy += y * y * weight; } // Adds a whole LLSQ. void LLSQ::add(const LLSQ& other) { total_weight += other.total_weight; sigx += other.sigx; // update accumulators sigy += other.sigy; sigxx += other.sigxx; sigxy += other.sigxy; sigyy += other.sigyy; } /********************************************************************** * LLSQ::remove * * Delete an element from the acculuator. **********************************************************************/ void LLSQ::remove(double x, double y) { // delete an element if (total_weight <= 0.0) // illegal EMPTY_LLSQ.error("LLSQ::remove", ABORT, NULL); total_weight--; // count elements sigx -= x; // update accumulators sigy -= y; sigxx -= x * x; sigxy -= x * y; sigyy -= y * y; } /********************************************************************** * LLSQ::m * * Return the gradient of the line fit. **********************************************************************/ double LLSQ::m() const { // get gradient double covar = covariance(); double x_var = x_variance(); if (x_var != 0.0) return covar / x_var; else return 0.0; // too little } /********************************************************************** * LLSQ::c * * Return the constant of the line fit. **********************************************************************/ double LLSQ::c(double m) const { // get constant if (total_weight > 0.0) return (sigy - m * sigx) / total_weight; else return 0; // too little } /********************************************************************** * LLSQ::rms * * Return the rms error of the fit. **********************************************************************/ double LLSQ::rms(double m, double c) const { // get error double error; // total error if (total_weight > 0) { error = sigyy + m * (m * sigxx + 2 * (c * sigx - sigxy)) + c * (total_weight * c - 2 * sigy); if (error >= 0) error = sqrt(error / total_weight); // sqrt of mean else error = 0; } else { error = 0; // too little } return error; } /********************************************************************** * LLSQ::pearson * * Return the pearson product moment correlation coefficient. **********************************************************************/ double LLSQ::pearson() const { // get correlation double r = 0.0; // Correlation is 0 if insufficent data. double covar = covariance(); if (covar != 0.0) { double var_product = x_variance() * y_variance(); if (var_product > 0.0) r = covar / sqrt(var_product); } return r; } // Returns the x,y means as an FCOORD. FCOORD LLSQ::mean_point() const { if (total_weight > 0.0) { return FCOORD(sigx / total_weight, sigy / total_weight); } else { return FCOORD(0.0f, 0.0f); } } // Returns the sqrt of the mean squared error measured perpendicular from the // line through mean_point() in the direction dir. // // Derivation: // Lemma: Let v and x_i (i=1..N) be a k-dimensional vectors (1xk matrices). // Let % be dot product and ' be transpose. Note that: // Sum[i=1..N] (v % x_i)^2 // = v * [x_1' x_2' ... x_N'] * [x_1' x_2' .. x_N']' * v' // If x_i have average 0 we have: // = v * (N * COVARIANCE_MATRIX(X)) * v' // Expanded for the case that k = 2, where we treat the dimensions // as x_i and y_i, this is: // = v * (N * [VAR(X), COV(X,Y); COV(X,Y) VAR(Y)]) * v' // Now, we are trying to calculate the mean squared error, where v is // perpendicular to our line of interest: // Mean squared error // = E [ (v % (x_i - x_avg))) ^2 ] // = Sum (v % (x_i - x_avg))^2 / N // = v * N * [VAR(X) COV(X,Y); COV(X,Y) VAR(Y)] / N * v' // = v * [VAR(X) COV(X,Y); COV(X,Y) VAR(Y)] * v' // = code below double LLSQ::rms_orth(const FCOORD &dir) const { FCOORD v = !dir; v.normalise(); return sqrt(v.x() * v.x() * x_variance() + 2 * v.x() * v.y() * covariance() + v.y() * v.y() * y_variance()); } // Returns the direction of the fitted line as a unit vector, using the // least mean squared perpendicular distance. The line runs through the // mean_point, i.e. a point p on the line is given by: // p = mean_point() + lambda * vector_fit() for some real number lambda. // Note that the result (0<=x<=1, -1<=y<=-1) is directionally ambiguous // and may be negated without changing its meaning. // Fitting a line m + ðœ†v to a set of N points Pi = (xi, yi), where // m is the mean point (ð, ð‚) and // v is the direction vector (cosðœƒ, sinðœƒ) // The perpendicular distance of each Pi from the line is: // (Pi - m) x v, where x is the scalar cross product. // Total squared error is thus: // E = ∑((xi - ð)sin𜃠- (yi - ð‚)cosðœƒ)² // = ∑(xi - ð)²sin²𜃠- 2∑(xi - ð)(yi - ð‚)sin𜃠cos𜃠+ ∑(yi - ð‚)²cos²𜃠// = NVar(xi)sin²𜃠- 2NCovar(xi, yi)sin𜃠cos𜃠+ NVar(yi)cos²𜃠(Eq 1) // where Var(xi) is the variance of xi, // and Covar(xi, yi) is the covariance of xi, yi. // Taking the derivative wrt 𜃠and setting to 0 to obtain the min/max: // 0 = 2NVar(xi)sin𜃠cos𜃠-2NCovar(xi, yi)(cos²𜃠- sin²ðœƒ) -2NVar(yi)sin𜃠cos𜃠// => Covar(xi, yi)(cos²𜃠- sin²ðœƒ) = (Var(xi) - Var(yi))sin𜃠cos𜃠// Using double angles: // 2Covar(xi, yi)cos2𜃠= (Var(xi) - Var(yi))sin2𜃠(Eq 2) // So 𜃠= 0.5 atan2(2Covar(xi, yi), Var(xi) - Var(yi)) (Eq 3) // Because it involves 2𜃠, Eq 2 has 2 solutions 90 degrees apart, but which // is the min and which is the max? From Eq1: // E/N = Var(xi)sin²𜃠- 2Covar(xi, yi)sin𜃠cos𜃠+ Var(yi)cos²𜃠// and 90 degrees away, using sin/cos equivalences: // E'/N = Var(xi)cos²𜃠+ 2Covar(xi, yi)sin𜃠cos𜃠+ Var(yi)sin²𜃠// The second error is smaller (making it the minimum) iff // E'/N < E/N ie: // (Var(xi) - Var(yi))(cos²𜃠- sin²ðœƒ) < -4Covar(xi, yi)sin𜃠cos𜃠// Using double angles: // (Var(xi) - Var(yi))cos2𜃠< -2Covar(xi, yi)sin2𜃠(InEq 1) // But atan2(2Covar(xi, yi), Var(xi) - Var(yi)) picks 2𜃠such that: // sgn(cos2ðœƒ) = sgn(Var(xi) - Var(yi)) and sgn(sin2ðœƒ) = sgn(Covar(xi, yi)) // so InEq1 can *never* be true, making the atan2 result *always* the min! // In the degenerate case, where Covar(xi, yi) = 0 AND Var(xi) = Var(yi), // the 2 solutions have equal error and the inequality is still false. // Therefore the solution really is as trivial as Eq 3. // This is equivalent to returning the Principal Component in PCA, or the // eigenvector corresponding to the largest eigenvalue in the covariance // matrix. However, atan2 is much simpler! The one reference I found that // uses this formula is http://web.mit.edu/18.06/www/Essays/tlsfit.pdf but // that is still a much more complex derivation. It seems Pearson had already // found this simple solution in 1901. // http://books.google.com/books?id=WXwvAQAAIAAJ&pg=PA559 FCOORD LLSQ::vector_fit() const { double x_var = x_variance(); double y_var = y_variance(); double covar = covariance(); double theta = 0.5 * atan2(2.0 * covar, x_var - y_var); FCOORD result(cos(theta), sin(theta)); return result; } tesseract-3.04.01/ccstruct/linlsq.h000066400000000000000000000113371266071204500171710ustar00rootroot00000000000000/********************************************************************** * File: linlsq.h (Formerly llsq.h) * Description: Linear Least squares fitting code. * Author: Ray Smith * Created: Thu Sep 12 08:44:51 BST 1991 * * (C) Copyright 1991, 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 TESSERACT_CCSTRUCT_LINLSQ_H_ #define TESSERACT_CCSTRUCT_LINLSQ_H_ #include "points.h" #include "params.h" class LLSQ { public: LLSQ() { // constructor clear(); // set to zeros } void clear(); // initialize // Adds an element with a weight of 1. void add(double x, double y); // Adds an element with a specified weight. void add(double x, double y, double weight); // Adds a whole LLSQ. void add(const LLSQ& other); // Deletes an element with a weight of 1. void remove(double x, double y); inT32 count() const { // no of elements return static_cast(total_weight + 0.5); } double m() const; // get gradient double c(double m) const; // get constant double rms(double m, double c) const; // get error double pearson() const; // get correlation coefficient. // Returns the x,y means as an FCOORD. FCOORD mean_point() const; // Returns the average sum of squared perpendicular error from a line // through mean_point() in the direction dir. double rms_orth(const FCOORD &dir) const; // Returns the direction of the fitted line as a unit vector, using the // least mean squared perpendicular distance. The line runs through the // mean_point, i.e. a point p on the line is given by: // p = mean_point() + lambda * vector_fit() for some real number lambda. // Note that the result (0<=x<=1, -1<=y<=-1) is directionally ambiguous // and may be negated without changing its meaning, since a line is only // unique to a range of pi radians. // Modernists prefer to think of this as an Eigenvalue problem, but // Pearson had the simple solution in 1901. // // Note that this is equivalent to returning the Principal Component in PCA, // or the eigenvector corresponding to the largest eigenvalue in the // covariance matrix. FCOORD vector_fit() const; // Returns the covariance. double covariance() const { if (total_weight > 0.0) return (sigxy - sigx * sigy / total_weight) / total_weight; else return 0.0; } double x_variance() const { if (total_weight > 0.0) return (sigxx - sigx * sigx / total_weight) / total_weight; else return 0.0; } double y_variance() const { if (total_weight > 0.0) return (sigyy - sigy * sigy / total_weight) / total_weight; else return 0.0; } private: double total_weight; // no of elements or sum of weights. double sigx; // sum of x double sigy; // sum of y double sigxx; // sum x squared double sigxy; // sum of xy double sigyy; // sum y squared }; // Returns the median value of the vector, given that the values are // circular, with the given modulus. Values may be signed or unsigned, // eg range from -pi to pi (modulus 2pi) or from 0 to 2pi (modulus 2pi). // NOTE that the array is shuffled, but the time taken is linear. // An assumption is made that most of the values are spread over no more than // half the range, but wrap-around is accounted for if the median is near // the wrap-around point. // Cannot be a member of GenericVector, as it makes heavy used of LLSQ. // T must be an integer or float/double type. template T MedianOfCircularValues(T modulus, GenericVector* v) { LLSQ stats; T halfrange = static_cast(modulus / 2); int num_elements = v->size(); for (int i = 0; i < num_elements; ++i) { stats.add((*v)[i], (*v)[i] + halfrange); } bool offset_needed = stats.y_variance() < stats.x_variance(); if (offset_needed) { for (int i = 0; i < num_elements; ++i) { (*v)[i] += halfrange; } } int median_index = v->choose_nth_item(num_elements / 2); if (offset_needed) { for (int i = 0; i < num_elements; ++i) { (*v)[i] -= halfrange; } } return (*v)[median_index]; } #endif // TESSERACT_CCSTRUCT_LINLSQ_H_ tesseract-3.04.01/ccstruct/matrix.cpp000066400000000000000000000131131266071204500175200ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: matrix.c (Formerly matrix.c) * Description: Ratings matrix code. (Used by associator) * Author: Mark Seaman, OCR Technology * Created: Wed May 16 13:18:47 1990 * Modified: Wed Mar 20 09:44: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 "matrix.h" #include "callcpp.h" #include "ratngs.h" #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 < dim && row < col + band_width; ++row) { BLOB_CHOICE_LIST* choices = get(col, row); if (choices != NULL) { BLOB_CHOICE_LIST* copy_choices = new BLOB_CHOICE_LIST; copy_choices->deep_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 3 choices)\n"); int dim = dimension(); int band_width = bandwidth(); int row, 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 (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())); ++counter; if (counter == 3) break; } tprintf("\t"); } else { tprintf(" \t"); } } tprintf("\n"); } } tesseract-3.04.01/ccstruct/matrix.h000066400000000000000000000312251266071204500171710ustar00rootroot00000000000000/* -*-C-*- ****************************************************************************** * * File: matrix.h (Formerly matrix.h) * Description: Ratings matrix code. (Used by associator) * Author: Mark Seaman, OCR Technology * Created: Wed May 16 13:22:06 1990 * Modified: Tue Mar 19 16:00:20 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 TESSERACT_CCSTRUCT_MATRIX_H__ #define TESSERACT_CCSTRUCT_MATRIX_H__ #include "kdpair.h" #include "unicharset.h" class BLOB_CHOICE_LIST; #define NOT_CLASSIFIED reinterpret_cast(NULL) // 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: // 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_]; for (int x = 0; x < dim1_; x++) for (int y = 0; y < dim2_; y++) this->put(x, y, empty_); } 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 = 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 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 = num_elements(); if (fread(array_, sizeof(*array_), size, fp) != size) return false; if (swap) { for (int i = 0; i < size; ++i) ReverseN(&array_[i], sizeof(array_[i])); } return true; } // Writes to the given file. Returns false in case of error. // Assumes a T::Serialize(FILE*) const function. bool SerializeClasses(FILE* fp) const { if (!SerializeSize(fp)) return false; if (!empty_.Serialize(fp)) return false; int size = num_elements(); for (int i = 0; i < size; ++i) { if (!array_[i].Serialize(fp)) return false; } return true; } // Reads from the given file. Returns false in case of error. // Assumes a T::DeSerialize(bool swap, FILE*) function. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerializeClasses(bool swap, FILE* fp) { if (!DeSerializeSize(swap, fp)) return false; if (!empty_.DeSerialize(swap, fp)) return false; int size = num_elements(); for (int i = 0; i < size; ++i) { if (!array_[i].DeSerialize(swap, fp)) return false; } return true; } // 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 (,). virtual int index(int column, int row) const { return (column * dim2_ + row); } // Put a list element into the matrix at a specific location. void put(int column, int row, const T& thing) { array_[this->index(column, row)] = thing; } // Get the item at a specified location from the matrix. T get(int column, int row) const { return array_[this->index(column, row)]; } // Return a reference to the element at the specified location. const T& operator()(int column, int row) const { return array_[this->index(column, row)]; } T& operator()(int column, int row) { return array_[this->index(column, row)]; } // Allow access using array[column][row]. NOTE that the indices are // in the same left-to-right order as the () indexing. 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() { int size = num_elements(); for (int i = 0; i < size; ++i) { T matrix_cell = array_[i]; if (matrix_cell != empty_) delete matrix_cell; } } protected: // Factored helper to serialize the size. bool SerializeSize(FILE* fp) const { inT32 size = dim1_; if (fwrite(&size, sizeof(size), 1, fp) != 1) return false; size = dim2_; if (fwrite(&size, sizeof(size), 1, fp) != 1) return false; return true; } // Factored helper to deserialize the size. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerializeSize(bool swap, FILE* fp) { inT32 size1, size2; if (fread(&size1, sizeof(size1), 1, fp) != 1) return false; if (fread(&size2, sizeof(size2), 1, fp) != 1) return false; if (swap) { ReverseN(&size1, sizeof(size1)); ReverseN(&size2, sizeof(size2)); } Resize(size1, size2, empty_); return true; } T* array_; T empty_; // The unused cell. int dim1_; // Size of the 1st dimension in indexing functions. int dim2_; // Size of the 2nd dimension in indexing functions. }; // 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 BandTriMatrix : public 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. 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; } // 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 BandTriMatrix { public: 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; }; struct MATRIX_COORD { static void Delete(void *arg) { 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 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__ tesseract-3.04.01/ccstruct/mod128.cpp000066400000000000000000000067241266071204500172400ustar00rootroot00000000000000/********************************************************************** * File: mod128.c (Formerly dir128.c) * Description: Code to convert a DIR128 to an ICOORD. * Author: Ray Smith * Created: Tue Oct 22 11:56:09 BST 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include "mod128.h" const inT16 idirtab[] = { 1000, 0, 998, 49, 995, 98, 989, 146, 980, 195, 970, 242, 956, 290, 941, 336, 923, 382, 903, 427, 881, 471, 857, 514, 831, 555, 803, 595, 773, 634, 740, 671, 707, 707, 671, 740, 634, 773, 595, 803, 555, 831, 514, 857, 471, 881, 427, 903, 382, 923, 336, 941, 290, 956, 242, 970, 195, 980, 146, 989, 98, 995, 49, 998, 0, 1000, -49, 998, -98, 995, -146, 989, -195, 980, -242, 970, -290, 956, -336, 941, -382, 923, -427, 903, -471, 881, -514, 857, -555, 831, -595, 803, -634, 773, -671, 740, -707, 707, -740, 671, -773, 634, -803, 595, -831, 555, -857, 514, -881, 471, -903, 427, -923, 382, -941, 336, -956, 290, -970, 242, -980, 195, -989, 146, -995, 98, -998, 49, -1000, 0, -998, -49, -995, -98, -989, -146, -980, -195, -970, -242, -956, -290, -941, -336, -923, -382, -903, -427, -881, -471, -857, -514, -831, -555, -803, -595, -773, -634, -740, -671, -707, -707, -671, -740, -634, -773, -595, -803, -555, -831, -514, -857, -471, -881, -427, -903, -382, -923, -336, -941, -290, -956, -242, -970, -195, -980, -146, -989, -98, -995, -49, -998, 0, -1000, 49, -998, 98, -995, 146, -989, 195, -980, 242, -970, 290, -956, 336, -941, 382, -923, 427, -903, 471, -881, 514, -857, 555, -831, 595, -803, 634, -773, 671, -740, 707, -707, 740, -671, 773, -634, 803, -595, 831, -555, 857, -514, 881, -471, 903, -427, 923, -382, 941, -336, 956, -290, 970, -242, 980, -195, 989, -146, 995, -98, 998, -49 }; const ICOORD *dirtab = (ICOORD *) idirtab; /********************************************************************** * DIR128::DIR128 * * Quantize the direction of an FCOORD to make a DIR128. **********************************************************************/ DIR128::DIR128( //from fcoord const FCOORD fc //vector to quantize ) { int high, low, current; //binary search low = 0; if (fc.y () == 0) { if (fc.x () >= 0) dir = 0; else dir = MODULUS / 2; return; } high = MODULUS; do { current = (high + low) / 2; if (dirtab[current] * fc >= 0) low = current; else high = current; } while (high - low > 1); dir = low; } /********************************************************************** * dir_to_gradient * * Convert a direction to a vector. **********************************************************************/ #if 0 // code is buggy for negative dir and unused ICOORD DIR128::vector() const { //convert to vector return dirtab[dir]; //easy really } #endif tesseract-3.04.01/ccstruct/mod128.h000066400000000000000000000053151266071204500167000ustar00rootroot00000000000000/********************************************************************** * File: mod128.h (Formerly dir128.h) * Description: Header for class which implements modulo arithmetic. * Author: Ray Smith * Created: Tue Mar 26 17:48:13 GMT 1991 * * (C) Copyright 1991, 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 MOD128_H #define MOD128_H #include "points.h" #define MODULUS 128 /*range of directions */ #define DIRBITS 7 //no of bits used #define DIRSCALE 1000 //length of vector class DLLSYM DIR128 { public: DIR128() { } //empty constructor DIR128( //constructor inT16 value) { //value to assign value %= MODULUS; //modulo arithmetic if (value < 0) value += MODULUS; //done properly dir = (inT8) value; } DIR128(const FCOORD fc); //quantize vector DIR128 & operator= ( //assign of inT16 inT16 value) { //value to assign value %= MODULUS; //modulo arithmetic if (value < 0) value += MODULUS; //done properly dir = (inT8) value; return *this; } inT8 operator- ( //subtraction const DIR128 & minus) const//for signed result { //result inT16 result = dir - minus.dir; if (result > MODULUS / 2) result -= MODULUS; //get in range else if (result < -MODULUS / 2) result += MODULUS; return (inT8) result; } DIR128 operator+ ( //addition const DIR128 & add) const //of itself { DIR128 result; //sum result = dir + add.dir; //let = do the work return result; } DIR128 & operator+= ( //same as + const DIR128 & add) { *this = dir + add.dir; //let = do the work return *this; } inT8 get_dir() const { //access function return dir; } ICOORD vector() const; //turn to vector private: inT8 dir; //a direction }; #endif tesseract-3.04.01/ccstruct/normalis.cpp000066400000000000000000000512671266071204500200540ustar00rootroot00000000000000/********************************************************************** * File: normalis.cpp (Formerly denorm.c) * Description: Code for the DENORM class. * Author: Ray Smith * Created: Thu Apr 23 09:22:43 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. * **********************************************************************/ #include "normalis.h" #include #include "allheaders.h" #include "blobs.h" #include "helpers.h" #include "matrix.h" #include "ocrblock.h" #include "unicharset.h" #include "werd.h" // Tolerance in pixels used for baseline and xheight on non-upper/lower scripts. const int kSloppyTolerance = 4; // Final tolerance in pixels added to the computed xheight range. const float kFinalPixelTolerance = 0.125f; DENORM::DENORM() { Init(); } DENORM::DENORM(const DENORM &src) { rotation_ = NULL; *this = src; } DENORM & DENORM::operator=(const DENORM & src) { Clear(); inverse_ = src.inverse_; predecessor_ = src.predecessor_; pix_ = src.pix_; block_ = src.block_; if (src.rotation_ == NULL) rotation_ = NULL; else rotation_ = new FCOORD(*src.rotation_); x_origin_ = src.x_origin_; y_origin_ = src.y_origin_; x_scale_ = src.x_scale_; y_scale_ = src.y_scale_; final_xshift_ = src.final_xshift_; final_yshift_ = src.final_yshift_; return *this; } DENORM::~DENORM() { Clear(); } // Initializes the denorm for a transformation. For details see the large // comment in normalis.h. // Arguments: // block: if not NULL, then this is the first transformation, and // block->re_rotation() needs to be used after the Denorm // transformation to get back to the image coords. // rotation: if not NULL, apply this rotation after translation to the // origin and scaling. (Usually a classify rotation.) // predecessor: if not NULL, then predecessor has been applied to the // input space and needs to be undone to complete the inverse. // The above pointers are not owned by this DENORM and are assumed to live // longer than this denorm, except rotation, which is deep copied on input. // // x_origin: The x origin which will be mapped to final_xshift in the result. // y_origin: The y origin which will be mapped to final_yshift in the result. // Added to result of row->baseline(x) if not NULL. // // x_scale: scale factor for the x-coordinate. // y_scale: scale factor for the y-coordinate. Ignored if segs is given. // Note that these scale factors apply to the same x and y system as the // x-origin and y-origin apply, ie after any block rotation, but before // the rotation argument is applied. // // final_xshift: The x component of the final translation. // final_yshift: The y component of the final translation. void DENORM::SetupNormalization(const BLOCK* block, const FCOORD* rotation, const DENORM* predecessor, float x_origin, float y_origin, float x_scale, float y_scale, float final_xshift, float final_yshift) { Clear(); block_ = block; if (rotation == NULL) rotation_ = NULL; else rotation_ = new FCOORD(*rotation); predecessor_ = predecessor; x_origin_ = x_origin; y_origin_ = y_origin; x_scale_ = x_scale; y_scale_ = y_scale; final_xshift_ = final_xshift; final_yshift_ = final_yshift; } // Helper for SetupNonLinear computes an image of shortest run-lengths from // the x/y edges provided. // Based on "A nonlinear normalization method for handprinted Kanji character // recognition -- line density equalization" by Hiromitsu Yamada et al. // Eg below is an O in a 1-pixel margin-ed bounding box and the corresponding // ______________ input x_coords and y_coords. // | _________ | // | | _ | | 1, 6 // | | | | | | 1, 3, 4, 6 // | | | | | | 1, 3, 4, 6 // | | | | | | 1, 3, 4, 6 // | | |_| | | 1, 3, 4, 6 // | |_________| | 1, 6 // |_____________| // E 1 1 1 1 1 E // m 7 7 2 7 7 m // p 6 p // t 7 t // y y // The output image contains the min of the x and y run-length (distance // between edges) at each coordinate in the image thus: // ______________ // |7 1_1_1_1_1 7| // |1|5 5 1 5 5|1| // |1|2 2|1|2 2|1| // |1|2 2|1|2 2|1| // |1|2 2|1|2 2|1| // |1|2 2|1|2 2|1| // |1|5_5_1_5_5|1| // |7_1_1_1_1_1_7| // Note that the input coords are all integer, so all partial pixels are dealt // with elsewhere. Although it is nice for outlines to be properly connected // and continuous, there is no requirement that they be as such, so they could // have been derived from a flaky source, such as greyscale. // This function works only within the provided box, and it is assumed that the // input x_coords and y_coords have already been translated to have the bottom- // left of box as the origin. Although an output, the minruns should have been // pre-initialized to be the same size as box. Each element will contain the // minimum of x and y run-length as shown above. static void ComputeRunlengthImage( const TBOX& box, const GenericVector >& x_coords, const GenericVector >& y_coords, GENERIC_2D_ARRAY* minruns) { int width = box.width(); int height = box.height(); ASSERT_HOST(minruns->dim1() == width); ASSERT_HOST(minruns->dim2() == height); // Set a 2-d image array to the run lengths at each pixel. for (int ix = 0; ix < width; ++ix) { int y = 0; for (int i = 0; i < y_coords[ix].size(); ++i) { int y_edge = ClipToRange(y_coords[ix][i], 0, height); int gap = y_edge - y; // Every pixel between the last and current edge get set to the gap. while (y < y_edge) { (*minruns)(ix, y) = gap; ++y; } } // Pretend there is a bounding box of edges all around the image. int gap = height - y; while (y < height) { (*minruns)(ix, y) = gap; ++y; } } // Now set the image pixels the the MIN of the x and y runlengths. for (int iy = 0; iy < height; ++iy) { int x = 0; for (int i = 0; i < x_coords[iy].size(); ++i) { int x_edge = ClipToRange(x_coords[iy][i], 0, width); int gap = x_edge - x; while (x < x_edge) { if (gap < (*minruns)(x, iy)) (*minruns)(x, iy) = gap; ++x; } } int gap = width - x; while (x < width) { if (gap < (*minruns)(x, iy)) (*minruns)(x, iy) = gap; ++x; } } } // Converts the run-length image (see above to the edge density profiles used // for scaling, thus: // ______________ // |7 1_1_1_1_1 7| = 5.28 // |1|5 5 1 5 5|1| = 3.8 // |1|2 2|1|2 2|1| = 5 // |1|2 2|1|2 2|1| = 5 // |1|2 2|1|2 2|1| = 5 // |1|2 2|1|2 2|1| = 5 // |1|5_5_1_5_5|1| = 3.8 // |7_1_1_1_1_1_7| = 5.28 // 6 4 4 8 4 4 6 // . . . . . . . // 2 4 4 0 4 4 2 // 8 8 // Each profile is the sum of the reciprocals of the pixels in the image in // the appropriate row or column, and these are then normalized to sum to 1. // On output hx, hy contain an extra element, which will eventually be used // to guarantee that the top/right edge of the box (and anything beyond) always // gets mapped to the maximum target coordinate. static void ComputeEdgeDensityProfiles(const TBOX& box, const GENERIC_2D_ARRAY& minruns, GenericVector* hx, GenericVector* hy) { int width = box.width(); int height = box.height(); hx->init_to_size(width + 1, 0.0); hy->init_to_size(height + 1, 0.0); double total = 0.0; for (int iy = 0; iy < height; ++iy) { for (int ix = 0; ix < width; ++ix) { int run = minruns(ix, iy); if (run == 0) run = 1; float density = 1.0f / run; (*hx)[ix] += density; (*hy)[iy] += density; } total += (*hy)[iy]; } // Normalize each profile to sum to 1. if (total > 0.0) { for (int ix = 0; ix < width; ++ix) { (*hx)[ix] /= total; } for (int iy = 0; iy < height; ++iy) { (*hy)[iy] /= total; } } // There is an extra element in each array, so initialize to 1. (*hx)[width] = 1.0f; (*hy)[height] = 1.0f; } // Sets up the DENORM to execute a non-linear transformation based on // preserving an even distribution of stroke edges. The transformation // operates only within the given box. // x_coords is a collection of the x-coords of vertical edges for each // y-coord starting at box.bottom(). // y_coords is a collection of the y-coords of horizontal edges for each // x-coord starting at box.left(). // Eg x_coords[0] is a collection of the x-coords of edges at y=bottom. // Eg x_coords[1] is a collection of the x-coords of edges at y=bottom + 1. // The second-level vectors must all be sorted in ascending order. // See comments on the helper functions above for more details. void DENORM::SetupNonLinear( const DENORM* predecessor, const TBOX& box, float target_width, float target_height, float final_xshift, float final_yshift, const GenericVector >& x_coords, const GenericVector >& y_coords) { Clear(); predecessor_ = predecessor; // x_map_ and y_map_ store a mapping from input x and y coordinate to output // x and y coordinate, based on scaling to the supplied target_width and // target_height. x_map_ = new GenericVector; y_map_ = new GenericVector; // Set a 2-d image array to the run lengths at each pixel. int width = box.width(); int height = box.height(); GENERIC_2D_ARRAY minruns(width, height, 0); ComputeRunlengthImage(box, x_coords, y_coords, &minruns); // Edge density is the sum of the inverses of the run lengths. Compute // edge density projection profiles. ComputeEdgeDensityProfiles(box, minruns, x_map_, y_map_); // Convert the edge density profiles to the coordinates by multiplying by // the desired size and accumulating. (*x_map_)[width] = target_width; for (int x = width - 1; x >= 0; --x) { (*x_map_)[x] = (*x_map_)[x + 1] - (*x_map_)[x] * target_width; } (*y_map_)[height] = target_height; for (int y = height - 1; y >= 0; --y) { (*y_map_)[y] = (*y_map_)[y + 1] - (*y_map_)[y] * target_height; } x_origin_ = box.left(); y_origin_ = box.bottom(); final_xshift_ = final_xshift; final_yshift_ = final_yshift; } // Transforms the given coords one step forward to normalized space, without // using any block rotation or predecessor. void DENORM::LocalNormTransform(const TPOINT& pt, TPOINT* transformed) const { FCOORD src_pt(pt.x, pt.y); FCOORD float_result; LocalNormTransform(src_pt, &float_result); transformed->x = IntCastRounded(float_result.x()); transformed->y = IntCastRounded(float_result.y()); } void DENORM::LocalNormTransform(const FCOORD& pt, FCOORD* transformed) const { FCOORD translated(pt.x() - x_origin_, pt.y() - y_origin_); if (x_map_ != NULL && y_map_ != NULL) { int x = ClipToRange(IntCastRounded(translated.x()), 0, x_map_->size()-1); translated.set_x((*x_map_)[x]); int y = ClipToRange(IntCastRounded(translated.y()), 0, y_map_->size()-1); translated.set_y((*y_map_)[y]); } else { translated.set_x(translated.x() * x_scale_); translated.set_y(translated.y() * y_scale_); if (rotation_ != NULL) translated.rotate(*rotation_); } transformed->set_x(translated.x() + final_xshift_); transformed->set_y(translated.y() + final_yshift_); } // Transforms the given coords forward to normalized space using the // full transformation sequence defined by the block rotation, the // predecessors, deepest first, and finally this. If first_norm is not NULL, // then the first and deepest transformation used is first_norm, ending // with this, and the block rotation will not be applied. void DENORM::NormTransform(const DENORM* first_norm, const TPOINT& pt, TPOINT* transformed) const { FCOORD src_pt(pt.x, pt.y); FCOORD float_result; NormTransform(first_norm, src_pt, &float_result); transformed->x = IntCastRounded(float_result.x()); transformed->y = IntCastRounded(float_result.y()); } void DENORM::NormTransform(const DENORM* first_norm, const FCOORD& pt, FCOORD* transformed) const { FCOORD src_pt(pt); if (first_norm != this) { if (predecessor_ != NULL) { predecessor_->NormTransform(first_norm, pt, &src_pt); } else if (block_ != NULL) { FCOORD fwd_rotation(block_->re_rotation().x(), -block_->re_rotation().y()); src_pt.rotate(fwd_rotation); } } LocalNormTransform(src_pt, transformed); } // Transforms the given coords one step back to source space, without // using to any block rotation or predecessor. void DENORM::LocalDenormTransform(const TPOINT& pt, TPOINT* original) const { FCOORD src_pt(pt.x, pt.y); FCOORD float_result; LocalDenormTransform(src_pt, &float_result); original->x = IntCastRounded(float_result.x()); original->y = IntCastRounded(float_result.y()); } void DENORM::LocalDenormTransform(const FCOORD& pt, FCOORD* original) const { FCOORD rotated(pt.x() - final_xshift_, pt.y() - final_yshift_); if (x_map_ != NULL && y_map_ != NULL) { int x = x_map_->binary_search(rotated.x()); original->set_x(x + x_origin_); int y = y_map_->binary_search(rotated.y()); original->set_y(y + y_origin_); } else { if (rotation_ != NULL) { FCOORD inverse_rotation(rotation_->x(), -rotation_->y()); rotated.rotate(inverse_rotation); } original->set_x(rotated.x() / x_scale_ + x_origin_); float y_scale = y_scale_; original->set_y(rotated.y() / y_scale + y_origin_); } } // Transforms the given coords all the way back to source image space using // the full transformation sequence defined by this and its predecessors // recursively, shallowest first, and finally any block re_rotation. // If last_denorm is not NULL, then the last transformation used will // be last_denorm, and the block re_rotation will never be executed. void DENORM::DenormTransform(const DENORM* last_denorm, const TPOINT& pt, TPOINT* original) const { FCOORD src_pt(pt.x, pt.y); FCOORD float_result; DenormTransform(last_denorm, src_pt, &float_result); original->x = IntCastRounded(float_result.x()); original->y = IntCastRounded(float_result.y()); } void DENORM::DenormTransform(const DENORM* last_denorm, const FCOORD& pt, FCOORD* original) const { LocalDenormTransform(pt, original); if (last_denorm != this) { if (predecessor_ != NULL) { predecessor_->DenormTransform(last_denorm, *original, original); } else if (block_ != NULL) { original->rotate(block_->re_rotation()); } } } // Normalize a blob using blob transformations. Less accurate, but // more accurately copies the old way. void DENORM::LocalNormBlob(TBLOB* blob) const { TBOX blob_box = blob->bounding_box(); ICOORD translation(-IntCastRounded(x_origin_), -IntCastRounded(y_origin_)); blob->Move(translation); if (y_scale_ != 1.0f) blob->Scale(y_scale_); if (rotation_ != NULL) blob->Rotate(*rotation_); translation.set_x(IntCastRounded(final_xshift_)); translation.set_y(IntCastRounded(final_yshift_)); blob->Move(translation); } // Fills in the x-height range accepted by the given unichar_id, given its // bounding box in the usual baseline-normalized coordinates, with some // initial crude x-height estimate (such as word size) and this denoting the // transformation that was used. void DENORM::XHeightRange(int unichar_id, const UNICHARSET& unicharset, const TBOX& bbox, float* min_xht, float* max_xht, float* yshift) const { // Default return -- accept anything. *yshift = 0.0f; *min_xht = 0.0f; *max_xht = MAX_FLOAT32; if (!unicharset.top_bottom_useful()) return; // Clip the top and bottom to the limit of normalized feature space. int top = ClipToRange(bbox.top(), 0, kBlnCellHeight - 1); int bottom = ClipToRange(bbox.bottom(), 0, kBlnCellHeight - 1); // A tolerance of yscale corresponds to 1 pixel in the image. double tolerance = y_scale(); // If the script doesn't have upper and lower-case characters, widen the // tolerance to allow sloppy baseline/x-height estimates. if (!unicharset.script_has_upper_lower()) tolerance = y_scale() * kSloppyTolerance; int min_bottom, max_bottom, min_top, max_top; unicharset.get_top_bottom(unichar_id, &min_bottom, &max_bottom, &min_top, &max_top); // Calculate the scale factor we'll use to get to image y-pixels double midx = (bbox.left() + bbox.right()) / 2.0; double ydiff = (bbox.top() - bbox.bottom()) + 2.0; FCOORD mid_bot(midx, bbox.bottom()), tmid_bot; FCOORD mid_high(midx, bbox.bottom() + ydiff), tmid_high; DenormTransform(NULL, mid_bot, &tmid_bot); DenormTransform(NULL, mid_high, &tmid_high); // bln_y_measure * yscale = image_y_measure double yscale = tmid_high.pt_to_pt_dist(tmid_bot) / ydiff; // Calculate y-shift int bln_yshift = 0, bottom_shift = 0, top_shift = 0; if (bottom < min_bottom - tolerance) { bottom_shift = bottom - min_bottom; } else if (bottom > max_bottom + tolerance) { bottom_shift = bottom - max_bottom; } if (top < min_top - tolerance) { top_shift = top - min_top; } else if (top > max_top + tolerance) { top_shift = top - max_top; } if ((top_shift >= 0 && bottom_shift > 0) || (top_shift < 0 && bottom_shift < 0)) { bln_yshift = (top_shift + bottom_shift) / 2; } *yshift = bln_yshift * yscale; // To help very high cap/xheight ratio fonts accept the correct x-height, // and to allow the large caps in small caps to accept the xheight of the // small caps, add kBlnBaselineOffset to chars with a maximum max, and have // a top already at a significantly high position. if (max_top == kBlnCellHeight - 1 && top > kBlnCellHeight - kBlnBaselineOffset / 2) max_top += kBlnBaselineOffset; top -= bln_yshift; int height = top - kBlnBaselineOffset; double min_height = min_top - kBlnBaselineOffset - tolerance; double max_height = max_top - kBlnBaselineOffset + tolerance; // We shouldn't try calculations if the characters are very short (for example // for punctuation). if (min_height > kBlnXHeight / 8 && height > 0) { float result = height * kBlnXHeight * yscale / min_height; *max_xht = result + kFinalPixelTolerance; result = height * kBlnXHeight * yscale / max_height; *min_xht = result - kFinalPixelTolerance; } } // Prints the content of the DENORM for debug purposes. void DENORM::Print() const { if (pix_ != NULL) { tprintf("Pix dimensions %d x %d x %d\n", pixGetWidth(pix_), pixGetHeight(pix_), pixGetDepth(pix_)); } if (inverse_) tprintf("Inverse\n"); if (block_ && block_->re_rotation().x() != 1.0f) { tprintf("Block rotation %g, %g\n", block_->re_rotation().x(), block_->re_rotation().y()); } tprintf("Input Origin = (%g, %g)\n", x_origin_, y_origin_); if (x_map_ != NULL && y_map_ != NULL) { tprintf("x map:\n"); for (int x = 0; x < x_map_->size(); ++x) { tprintf("%g ", (*x_map_)[x]); } tprintf("\ny map:\n"); for (int y = 0; y < y_map_->size(); ++y) { tprintf("%g ", (*y_map_)[y]); } tprintf("\n"); } else { tprintf("Scale = (%g, %g)\n", x_scale_, y_scale_); if (rotation_ != NULL) tprintf("Rotation = (%g, %g)\n", rotation_->x(), rotation_->y()); } tprintf("Final Origin = (%g, %g)\n", final_xshift_, final_xshift_); if (predecessor_ != NULL) { tprintf("Predecessor:\n"); predecessor_->Print(); } } // ============== Private Code ====================== // Free allocated memory and clear pointers. void DENORM::Clear() { if (x_map_ != NULL) { delete x_map_; x_map_ = NULL; } if (y_map_ != NULL) { delete y_map_; y_map_ = NULL; } if (rotation_ != NULL) { delete rotation_; rotation_ = NULL; } } // Setup default values. void DENORM::Init() { inverse_ = false; pix_ = NULL; block_ = NULL; rotation_ = NULL; predecessor_ = NULL; x_map_ = NULL; y_map_ = NULL; x_origin_ = 0.0f; y_origin_ = 0.0f; x_scale_ = 1.0f; y_scale_ = 1.0f; final_xshift_ = 0.0f; final_yshift_ = static_cast(kBlnBaselineOffset); } tesseract-3.04.01/ccstruct/normalis.h000066400000000000000000000343261266071204500175160ustar00rootroot00000000000000/********************************************************************** * File: normalis.h (Formerly denorm.h) * Description: Code for the DENORM class. * Author: Ray Smith * Created: Thu Apr 23 09:22:43 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 NORMALIS_H #define NORMALIS_H #include #include "genericvector.h" #include "host.h" const int kBlnCellHeight = 256; // Full-height for baseline normalization. const int kBlnXHeight = 128; // x-height for baseline normalization. const int kBlnBaselineOffset = 64; // offset for baseline normalization. struct Pix; class ROW; // Forward decl class BLOCK; class FCOORD; struct TBLOB; class TBOX; struct TPOINT; class UNICHARSET; namespace tesseract { // Possible normalization methods. Use NEGATIVE values as these also // double up as markers for the last sub-classifier. enum NormalizationMode { NM_BASELINE = -3, // The original BL normalization mode. NM_CHAR_ISOTROPIC = -2, // Character normalization but isotropic. NM_CHAR_ANISOTROPIC = -1 // The original CN normalization mode. }; } // namespace tesseract. class DENORM { public: DENORM(); // Copying a DENORM is allowed. DENORM(const DENORM &); DENORM& operator=(const DENORM&); ~DENORM(); // Setup the normalization transformation parameters. // The normalizations applied to a blob are as follows: // 1. An optional block layout rotation that was applied during layout // analysis to make the textlines horizontal. // 2. A normalization transformation (LocalNormTransform): // Subtract the "origin" // Apply an x,y scaling. // Apply an optional rotation. // Add back a final translation. // The origin is in the block-rotated space, and is usually something like // the x-middle of the word at the baseline. // 3. Zero or more further normalization transformations that are applied // in sequence, with a similar pattern to the first normalization transform. // // A DENORM holds the parameters of a single normalization, and can execute // both the LocalNormTransform (a forwards normalization), and the // LocalDenormTransform which is an inverse transform or de-normalization. // A DENORM may point to a predecessor DENORM, which is actually the earlier // normalization, so the full normalization sequence involves executing all // predecessors first and then the transform in "this". // Let x be image co-ordinates and that we have normalization classes A, B, C // where we first apply A then B then C to get normalized x': // x' = CBAx // Then the backwards (to original coordinates) would be: // x = A^-1 B^-1 C^-1 x' // and A = B->predecessor_ and B = C->predecessor_ // NormTransform executes all predecessors recursively, and then this. // NormTransform would be used to transform an image-based feature to // normalized space for use in a classifier // DenormTransform inverts this and then all predecessors. It can be // used to get back to the original image coordinates from normalized space. // The LocalNormTransform member executes just the transformation // in "this" without the layout rotation or any predecessors. It would be // used to run each successive normalization, eg the word normalization, // and later the character normalization. // Arguments: // block: if not NULL, then this is the first transformation, and // block->re_rotation() needs to be used after the Denorm // transformation to get back to the image coords. // rotation: if not NULL, apply this rotation after translation to the // origin and scaling. (Usually a classify rotation.) // predecessor: if not NULL, then predecessor has been applied to the // input space and needs to be undone to complete the inverse. // The above pointers are not owned by this DENORM and are assumed to live // longer than this denorm, except rotation, which is deep copied on input. // // x_origin: The x origin which will be mapped to final_xshift in the result. // y_origin: The y origin which will be mapped to final_yshift in the result. // Added to result of row->baseline(x) if not NULL. // // x_scale: scale factor for the x-coordinate. // y_scale: scale factor for the y-coordinate. Ignored if segs is given. // Note that these scale factors apply to the same x and y system as the // x-origin and y-origin apply, ie after any block rotation, but before // the rotation argument is applied. // // final_xshift: The x component of the final translation. // final_yshift: The y component of the final translation. // // In theory, any of the commonly used normalizations can be setup here: // * Traditional baseline normalization on a word: // SetupNormalization(block, NULL, NULL, // box.x_middle(), baseline, // kBlnXHeight / x_height, kBlnXHeight / x_height, // 0, kBlnBaselineOffset); // * "Numeric mode" baseline normalization on a word, in which the blobs // are positioned with the bottom as the baseline is achieved by making // a separate DENORM for each blob. // SetupNormalization(block, NULL, NULL, // box.x_middle(), box.bottom(), // kBlnXHeight / x_height, kBlnXHeight / x_height, // 0, kBlnBaselineOffset); // * Anisotropic character normalization used by IntFx. // SetupNormalization(NULL, NULL, denorm, // centroid_x, centroid_y, // 51.2 / ry, 51.2 / rx, 128, 128); // * Normalize blob height to x-height (current OSD): // SetupNormalization(NULL, &rotation, NULL, // box.rotational_x_middle(rotation), // box.rotational_y_middle(rotation), // kBlnXHeight / box.rotational_height(rotation), // kBlnXHeight / box.rotational_height(rotation), // 0, kBlnBaselineOffset); // * Secondary normalization for classification rotation (current): // FCOORD rotation = block->classify_rotation(); // float target_height = kBlnXHeight / CCStruct::kXHeightCapRatio; // SetupNormalization(NULL, &rotation, denorm, // box.rotational_x_middle(rotation), // box.rotational_y_middle(rotation), // target_height / box.rotational_height(rotation), // target_height / box.rotational_height(rotation), // 0, kBlnBaselineOffset); // * Proposed new normalizations for CJK: Between them there is then // no need for further normalization at all, and the character fills the cell. // ** Replacement for baseline normalization on a word: // Scales height and width independently so that modal height and pitch // fill the cell respectively. // float cap_height = x_height / CCStruct::kXHeightCapRatio; // SetupNormalization(block, NULL, NULL, // box.x_middle(), cap_height / 2.0f, // kBlnCellHeight / fixed_pitch, // kBlnCellHeight / cap_height, // 0, 0); // ** Secondary normalization for classification (with rotation) (proposed): // Requires a simple translation to the center of the appropriate character // cell, no further scaling and a simple rotation (or nothing) about the // cell center. // FCOORD rotation = block->classify_rotation(); // SetupNormalization(NULL, &rotation, denorm, // fixed_pitch_cell_center, // 0.0f, // 1.0f, // 1.0f, // 0, 0); void SetupNormalization(const BLOCK* block, const FCOORD* rotation, const DENORM* predecessor, float x_origin, float y_origin, float x_scale, float y_scale, float final_xshift, float final_yshift); // Sets up the DENORM to execute a non-linear transformation based on // preserving an even distribution of stroke edges. The transformation // operates only within the given box, scaling input coords within the box // non-linearly to a box of target_width by target_height, with all other // coords being clipped to the box edge. As with SetupNormalization above, // final_xshift and final_yshift are applied after scaling, and the bottom- // left of box is used as a pre-scaling origin. // x_coords is a collection of the x-coords of vertical edges for each // y-coord starting at box.bottom(). // y_coords is a collection of the y-coords of horizontal edges for each // x-coord starting at box.left(). // Eg x_coords[0] is a collection of the x-coords of edges at y=bottom. // Eg x_coords[1] is a collection of the x-coords of edges at y=bottom + 1. // The second-level vectors must all be sorted in ascending order. void SetupNonLinear(const DENORM* predecessor, const TBOX& box, float target_width, float target_height, float final_xshift, float final_yshift, const GenericVector >& x_coords, const GenericVector >& y_coords); // Transforms the given coords one step forward to normalized space, without // using any block rotation or predecessor. void LocalNormTransform(const TPOINT& pt, TPOINT* transformed) const; void LocalNormTransform(const FCOORD& pt, FCOORD* transformed) const; // Transforms the given coords forward to normalized space using the // full transformation sequence defined by the block rotation, the // predecessors, deepest first, and finally this. If first_norm is not NULL, // then the first and deepest transformation used is first_norm, ending // with this, and the block rotation will not be applied. void NormTransform(const DENORM* first_norm, const TPOINT& pt, TPOINT* transformed) const; void NormTransform(const DENORM* first_norm, const FCOORD& pt, FCOORD* transformed) const; // Transforms the given coords one step back to source space, without // using to any block rotation or predecessor. void LocalDenormTransform(const TPOINT& pt, TPOINT* original) const; void LocalDenormTransform(const FCOORD& pt, FCOORD* original) const; // Transforms the given coords all the way back to source image space using // the full transformation sequence defined by this and its predecessors // recursively, shallowest first, and finally any block re_rotation. // If last_denorm is not NULL, then the last transformation used will // be last_denorm, and the block re_rotation will never be executed. void DenormTransform(const DENORM* last_denorm, const TPOINT& pt, TPOINT* original) const; void DenormTransform(const DENORM* last_denorm, const FCOORD& pt, FCOORD* original) const; // Normalize a blob using blob transformations. Less accurate, but // more accurately copies the old way. void LocalNormBlob(TBLOB* blob) const; // Fills in the x-height range accepted by the given unichar_id in blob // coordinates, given its bounding box in the usual baseline-normalized // coordinates, with some initial crude x-height estimate (such as word // size) and this denoting the transformation that was used. // Also returns the amount the character must have shifted up or down. void XHeightRange(int unichar_id, const UNICHARSET& unicharset, const TBOX& bbox, float* min_xht, float* max_xht, float* yshift) const; // Prints the content of the DENORM for debug purposes. void Print() const; Pix* pix() const { return pix_; } void set_pix(Pix* pix) { pix_ = pix; } bool inverse() const { return inverse_; } void set_inverse(bool value) { inverse_ = value; } const DENORM* RootDenorm() const { if (predecessor_ != NULL) return predecessor_->RootDenorm(); return this; } const DENORM* predecessor() const { return predecessor_; } // Accessors - perhaps should not be needed. float x_scale() const { return x_scale_; } float y_scale() const { return y_scale_; } const BLOCK* block() const { return block_; } void set_block(const BLOCK* block) { block_ = block; } private: // Free allocated memory and clear pointers. void Clear(); // Setup default values. void Init(); // Best available image. Pix* pix_; // True if the source image is white-on-black. bool inverse_; // Block the word came from. If not null, block->re_rotation() takes the // "untransformed" coordinates even further back to the original image. // Used only on the first DENORM in a chain. const BLOCK* block_; // Rotation to apply between translation to the origin and scaling. const FCOORD* rotation_; // Previous transformation in a chain. const DENORM* predecessor_; // Non-linear transformation maps directly from each integer offset from the // origin to the corresponding x-coord. Owned by the DENORM. GenericVector* x_map_; // Non-linear transformation maps directly from each integer offset from the // origin to the corresponding y-coord. Owned by the DENORM. GenericVector* y_map_; // x-coordinate to be mapped to final_xshift_ in the result. float x_origin_; // y-coordinate to be mapped to final_yshift_ in the result. float y_origin_; // Scale factors for x and y coords. Applied to pre-rotation system. float x_scale_; float y_scale_; // Destination coords of the x_origin_ and y_origin_. float final_xshift_; float final_yshift_; }; #endif tesseract-3.04.01/ccstruct/ocrblock.cpp000066400000000000000000000436721266071204500200270ustar00rootroot00000000000000/********************************************************************** * File: ocrblock.cpp (Formerly block.c) * Description: BLOCK member functions and iterator functions. * Author: Ray Smith * Created: Fri Mar 15 09:41:28 GMT 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include #include "blckerr.h" #include "ocrblock.h" #include "stepblob.h" #include "tprintf.h" #define BLOCK_LABEL_HEIGHT 150 //char height of block id ELISTIZE (BLOCK) /** * BLOCK::BLOCK * * Constructor for a simple rectangular block. */ BLOCK::BLOCK(const char *name, //< filename BOOL8 prop, //< proportional inT16 kern, //< kerning inT16 space, //< spacing inT16 xmin, //< bottom left inT16 ymin, inT16 xmax, //< top right inT16 ymax) : PDBLK (xmin, ymin, xmax, ymax), filename(name), re_rotation_(1.0f, 0.0f), classify_rotation_(1.0f, 0.0f), skew_(1.0f, 0.0f) { ICOORDELT_IT left_it = &leftside; ICOORDELT_IT right_it = &rightside; proportional = prop; right_to_left_ = false; kerning = kern; spacing = space; font_class = -1; //not assigned cell_over_xheight_ = 2.0f; hand_poly = NULL; left_it.set_to_list (&leftside); right_it.set_to_list (&rightside); //make default box left_it.add_to_end (new ICOORDELT (xmin, ymin)); left_it.add_to_end (new ICOORDELT (xmin, ymax)); right_it.add_to_end (new ICOORDELT (xmax, ymin)); right_it.add_to_end (new ICOORDELT (xmax, ymax)); } /** * decreasing_top_order * * Sort Comparator: Return <0 if row1 top < row2 top */ int decreasing_top_order( // const void *row1, const void *row2) { return (*(ROW **) row2)->bounding_box ().top () - (*(ROW **) row1)->bounding_box ().top (); } /** * BLOCK::rotate * * Rotate the polygon by the given rotation and recompute the bounding_box. */ void BLOCK::rotate(const FCOORD& rotation) { poly_block()->rotate(rotation); box = *poly_block()->bounding_box(); } // Returns the bounding box including the desired combination of upper and // lower noise/diacritic elements. TBOX BLOCK::restricted_bounding_box(bool upper_dots, bool lower_dots) const { TBOX box; // This is a read-only iteration of the rows in the block. ROW_IT it(const_cast(&rows)); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { box += it.data()->restricted_bounding_box(upper_dots, lower_dots); } return box; } /** * BLOCK::reflect_polygon_in_y_axis * * Reflects the polygon in the y-axis and recompute the bounding_box. * Does nothing to any contained rows/words/blobs etc. */ void BLOCK::reflect_polygon_in_y_axis() { poly_block()->reflect_in_y_axis(); box = *poly_block()->bounding_box(); } /** * BLOCK::sort_rows * * Order rows so that they are in order of decreasing Y coordinate */ void BLOCK::sort_rows() { // order on "top" ROW_IT row_it(&rows); row_it.sort (decreasing_top_order); } /** * BLOCK::compress * * Delete space between the rows. (And maybe one day, compress the rows) * Fill space of block from top down, left aligning rows. */ void BLOCK::compress() { // squash it up #define ROW_SPACING 5 ROW_IT row_it(&rows); ROW *row; ICOORD row_spacing (0, ROW_SPACING); ICOORDELT_IT icoordelt_it; sort_rows(); box = TBOX (box.topleft (), box.topleft ()); box.move_bottom_edge (ROW_SPACING); for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { row = row_it.data (); row->move (box.botleft () - row_spacing - row->bounding_box ().topleft ()); box += row->bounding_box (); } leftside.clear (); icoordelt_it.set_to_list (&leftside); icoordelt_it.add_to_end (new ICOORDELT (box.left (), box.bottom ())); icoordelt_it.add_to_end (new ICOORDELT (box.left (), box.top ())); rightside.clear (); icoordelt_it.set_to_list (&rightside); icoordelt_it.add_to_end (new ICOORDELT (box.right (), box.bottom ())); icoordelt_it.add_to_end (new ICOORDELT (box.right (), box.top ())); } /** * BLOCK::check_pitch * * Check whether the block is fixed or prop, set the flag, and set * the pitch if it is fixed. */ void BLOCK::check_pitch() { // check prop // tprintf("Missing FFT fixed pitch stuff!\n"); pitch = -1; } /** * BLOCK::compress * * Compress and move in a single operation. */ void BLOCK::compress( // squash it up const ICOORD vec // and move ) { box.move (vec); compress(); } /** * BLOCK::print * * Print the info on a block */ void BLOCK::print( //print list of sides FILE *, //< file to print on BOOL8 dump //< print full detail ) { ICOORDELT_IT it = &leftside; //iterator box.print (); tprintf ("Proportional= %s\n", proportional ? "TRUE" : "FALSE"); tprintf ("Kerning= %d\n", kerning); tprintf ("Spacing= %d\n", spacing); tprintf ("Fixed_pitch=%d\n", pitch); tprintf ("Filename= %s\n", filename.string ()); if (dump) { tprintf ("Left side coords are:\n"); for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) tprintf ("(%d,%d) ", it.data ()->x (), it.data ()->y ()); tprintf ("\n"); tprintf ("Right side coords are:\n"); it.set_to_list (&rightside); for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) tprintf ("(%d,%d) ", it.data ()->x (), it.data ()->y ()); tprintf ("\n"); } } /** * BLOCK::operator= * * Assignment - duplicate the block structure, but with an EMPTY row list. */ BLOCK & BLOCK::operator= ( //assignment const BLOCK & source //from this ) { this->ELIST_LINK::operator= (source); this->PDBLK::operator= (source); proportional = source.proportional; kerning = source.kerning; spacing = source.spacing; filename = source.filename; //STRINGs assign ok if (!rows.empty ()) rows.clear (); re_rotation_ = source.re_rotation_; classify_rotation_ = source.classify_rotation_; skew_ = source.skew_; return *this; } // This function is for finding the approximate (horizontal) distance from // the x-coordinate of the left edge of a symbol to the left edge of the // text block which contains it. We are passed: // segments - output of PB_LINE_IT::get_line() which contains x-coordinate // intervals for the scan line going through the symbol's y-coordinate. // Each element of segments is of the form (x()=start_x, y()=length). // x - the x coordinate of the symbol we're interested in. // margin - return value, the distance from x,y to the left margin of the // block containing it. // If all segments were to the right of x, we return false and 0. bool LeftMargin(ICOORDELT_LIST *segments, int x, int *margin) { bool found = false; *margin = 0; if (segments->empty()) return found; ICOORDELT_IT seg_it(segments); for (seg_it.mark_cycle_pt(); !seg_it.cycled_list(); seg_it.forward()) { int cur_margin = x - seg_it.data()->x(); if (cur_margin >= 0) { if (!found) { *margin = cur_margin; } else if (cur_margin < *margin) { *margin = cur_margin; } found = true; } } return found; } // This function is for finding the approximate (horizontal) distance from // the x-coordinate of the right edge of a symbol to the right edge of the // text block which contains it. We are passed: // segments - output of PB_LINE_IT::get_line() which contains x-coordinate // intervals for the scan line going through the symbol's y-coordinate. // Each element of segments is of the form (x()=start_x, y()=length). // x - the x coordinate of the symbol we're interested in. // margin - return value, the distance from x,y to the right margin of the // block containing it. // If all segments were to the left of x, we return false and 0. bool RightMargin(ICOORDELT_LIST *segments, int x, int *margin) { bool found = false; *margin = 0; if (segments->empty()) return found; ICOORDELT_IT seg_it(segments); for (seg_it.mark_cycle_pt(); !seg_it.cycled_list(); seg_it.forward()) { int cur_margin = seg_it.data()->x() + seg_it.data()->y() - x; if (cur_margin >= 0) { if (!found) { *margin = cur_margin; } else if (cur_margin < *margin) { *margin = cur_margin; } found = true; } } return found; } // Compute the distance from the left and right ends of each row to the // left and right edges of the block's polyblock. Illustration: // ____________________________ _______________________ // | Howdy neighbor! | |rectangular blocks look| // | This text is written to| |more like stacked pizza| // |illustrate how useful poly- |boxes. | // |blobs are in ----------- ------ The polyblob| // |dealing with| _________ |for a BLOCK rec-| // |harder layout| /===========\ |ords the possibly| // |issues. | | _ _ | |skewed pseudo-| // | You see this| | |_| \|_| | |rectangular | // |text is flowed| | } | |boundary that| // |around a mid-| \ ____ | |forms the ideal-| // |cloumn portrait._____ \ / __|ized text margin| // | Polyblobs exist| \ / |from which we should| // |to account for insets| | | |measure paragraph| // |which make otherwise| ----- |indentation. | // ----------------------- ---------------------- // // If we identify a drop-cap, we measure the left margin for the lines // below the first line relative to one space past the drop cap. The // first line's margin and those past the drop cap area are measured // relative to the enclosing polyblock. // // TODO(rays): Before this will work well, we'll need to adjust the // polyblob tighter around the text near images, as in: // UNLV_AUTO:mag.3G0 page 2 // UNLV_AUTO:mag.3G4 page 16 void BLOCK::compute_row_margins() { if (row_list()->empty() || row_list()->singleton()) { return; } // If Layout analysis was not called, default to this. POLY_BLOCK rect_block(bounding_box(), PT_FLOWING_TEXT); POLY_BLOCK *pblock = &rect_block; if (poly_block() != NULL) { pblock = poly_block(); } // Step One: Determine if there is a drop-cap. // TODO(eger): Fix up drop cap code for RTL languages. ROW_IT r_it(row_list()); ROW *first_row = r_it.data(); ROW *second_row = r_it.data_relative(1); // initialize the bottom of a fictitious drop cap far above the first line. int drop_cap_bottom = first_row->bounding_box().top() + first_row->bounding_box().height(); int drop_cap_right = first_row->bounding_box().left(); int mid_second_line = second_row->bounding_box().top() - second_row->bounding_box().height() / 2; WERD_IT werd_it(r_it.data()->word_list()); // words of line one if (!werd_it.empty()) { C_BLOB_IT cblob_it(werd_it.data()->cblob_list()); for (cblob_it.mark_cycle_pt(); !cblob_it.cycled_list(); cblob_it.forward()) { TBOX bbox = cblob_it.data()->bounding_box(); if (bbox.bottom() <= mid_second_line) { // we found a real drop cap first_row->set_has_drop_cap(true); if (drop_cap_bottom > bbox.bottom()) drop_cap_bottom = bbox.bottom(); if (drop_cap_right < bbox.right()) drop_cap_right = bbox.right(); } } } // Step Two: Calculate the margin from the text of each row to the block // (or drop-cap) boundaries. PB_LINE_IT lines(pblock); r_it.set_to_list(row_list()); for (r_it.mark_cycle_pt(); !r_it.cycled_list(); r_it.forward()) { ROW *row = r_it.data(); TBOX row_box = row->bounding_box(); int left_y = row->base_line(row_box.left()) + row->x_height(); int left_margin; ICOORDELT_LIST *segments = lines.get_line(left_y); LeftMargin(segments, row_box.left(), &left_margin); delete segments; if (row_box.top() >= drop_cap_bottom) { int drop_cap_distance = row_box.left() - row->space() - drop_cap_right; if (drop_cap_distance < 0) drop_cap_distance = 0; if (drop_cap_distance < left_margin) left_margin = drop_cap_distance; } int right_y = row->base_line(row_box.right()) + row->x_height(); int right_margin; segments = lines.get_line(right_y); RightMargin(segments, row_box.right(), &right_margin); delete segments; row->set_lmargin(left_margin); row->set_rmargin(right_margin); } } /********************************************************************** * PrintSegmentationStats * * Prints segmentation stats for the given block list. **********************************************************************/ void PrintSegmentationStats(BLOCK_LIST* block_list) { int num_blocks = 0; int num_rows = 0; int num_words = 0; int num_blobs = 0; BLOCK_IT block_it(block_list); for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) { BLOCK* block = block_it.data(); ++num_blocks; ROW_IT row_it(block->row_list()); for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { ++num_rows; ROW* row = row_it.data(); // Iterate over all werds in the row. WERD_IT werd_it(row->word_list()); for (werd_it.mark_cycle_pt(); !werd_it.cycled_list(); werd_it.forward()) { WERD* werd = werd_it.data(); ++num_words; num_blobs += werd->cblob_list()->length(); } } } tprintf("Block list stats:\nBlocks = %d\nRows = %d\nWords = %d\nBlobs = %d\n", num_blocks, num_rows, num_words, num_blobs); } /********************************************************************** * ExtractBlobsFromSegmentation * * Extracts blobs from the given block list and adds them to the output list. * The block list must have been created by performing a page segmentation. **********************************************************************/ void ExtractBlobsFromSegmentation(BLOCK_LIST* blocks, C_BLOB_LIST* output_blob_list) { C_BLOB_IT return_list_it(output_blob_list); BLOCK_IT block_it(blocks); for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) { BLOCK* block = block_it.data(); ROW_IT row_it(block->row_list()); for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { ROW* row = row_it.data(); // Iterate over all werds in the row. WERD_IT werd_it(row->word_list()); for (werd_it.mark_cycle_pt(); !werd_it.cycled_list(); werd_it.forward()) { WERD* werd = werd_it.data(); return_list_it.move_to_last(); return_list_it.add_list_after(werd->cblob_list()); return_list_it.move_to_last(); return_list_it.add_list_after(werd->rej_cblob_list()); } } } } /********************************************************************** * RefreshWordBlobsFromNewBlobs() * * Refreshes the words in the block_list by using blobs in the * new_blobs list. * Block list must have word segmentation in it. * It consumes the blobs provided in the new_blobs list. The blobs leftover in * the new_blobs list after the call weren't matched to any blobs of the words * in block list. * The output not_found_blobs is a list of blobs from the original segmentation * in the block_list for which no corresponding new blobs were found. **********************************************************************/ void RefreshWordBlobsFromNewBlobs(BLOCK_LIST* block_list, C_BLOB_LIST* new_blobs, C_BLOB_LIST* not_found_blobs) { // Now iterate over all the blobs in the segmentation_block_list_, and just // replace the corresponding c-blobs inside the werds. 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()) { ROW* row = row_it.data(); // Iterate over all werds in the row. WERD_IT werd_it(row->word_list()); WERD_LIST new_words; WERD_IT new_words_it(&new_words); for (werd_it.mark_cycle_pt(); !werd_it.cycled_list(); werd_it.forward()) { WERD* werd = werd_it.extract(); WERD* new_werd = werd->ConstructWerdWithNewBlobs(new_blobs, not_found_blobs); if (new_werd) { // Insert this new werd into the actual row's werd-list. Remove the // existing one. new_words_it.add_after_then_move(new_werd); delete werd; } else { // Reinsert the older word back, for lack of better options. // This is critical since dropping the words messes up segmentation: // eg. 1st word in the row might otherwise have W_FUZZY_NON turned on. new_words_it.add_after_then_move(werd); } } // Get rid of the old word list & replace it with the new one. row->word_list()->clear(); werd_it.move_to_first(); werd_it.add_list_after(&new_words); } } } tesseract-3.04.01/ccstruct/ocrblock.h000066400000000000000000000156521266071204500174710ustar00rootroot00000000000000/********************************************************************** * File: ocrblock.h (Formerly block.h) * Description: Page block class definition. * Author: Ray Smith * Created: Thu Mar 14 17:32:01 GMT 1991 * * (C) Copyright 1991, 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 OCRBLOCK_H #define OCRBLOCK_H #include "ocrpara.h" #include "ocrrow.h" #include "pdblock.h" class BLOCK; //forward decl ELISTIZEH (BLOCK) class BLOCK:public ELIST_LINK, public PDBLK //page block { friend class BLOCK_RECT_IT; //block iterator public: BLOCK() : re_rotation_(1.0f, 0.0f), classify_rotation_(1.0f, 0.0f), skew_(1.0f, 0.0f) { right_to_left_ = false; hand_poly = NULL; } BLOCK(const char *name, //< filename BOOL8 prop, //< proportional inT16 kern, //< kerning inT16 space, //< spacing inT16 xmin, //< bottom left inT16 ymin, inT16 xmax, //< top right inT16 ymax); ~BLOCK () { } /** * set space size etc. * @param prop proportional * @param kern inter char size * @param space inter word size * @param ch_pitch pitch if fixed */ void set_stats(BOOL8 prop, inT16 kern, inT16 space, inT16 ch_pitch) { proportional = prop; kerning = (inT8) kern; spacing = space; pitch = ch_pitch; } /// set char size void set_xheight(inT32 height) { xheight = height; } /// set font class void set_font_class(inT16 font) { font_class = font; } /// return proportional BOOL8 prop() const { return proportional; } bool right_to_left() const { return right_to_left_; } void set_right_to_left(bool value) { right_to_left_ = value; } /// return pitch inT32 fixed_pitch() const { return pitch; } /// return kerning inT16 kern() const { return kerning; } /// return font class inT16 font() const { return font_class; } /// return spacing inT16 space() const { return spacing; } /// return filename const char *name() const { return filename.string (); } /// return xheight inT32 x_height() const { return xheight; } float cell_over_xheight() const { return cell_over_xheight_; } void set_cell_over_xheight(float ratio) { cell_over_xheight_ = ratio; } /// get rows ROW_LIST *row_list() { return &rows; } // Compute the margins between the edges of each row and this block's // polyblock, and store the results in the rows. void compute_row_margins(); // get paragraphs PARA_LIST *para_list() { return ¶s_; } /// get blobs C_BLOB_LIST *blob_list() { return &c_blobs; } C_BLOB_LIST *reject_blobs() { return &rej_blobs; } FCOORD re_rotation() const { return re_rotation_; // How to transform coords back to image. } void set_re_rotation(const FCOORD& rotation) { re_rotation_ = rotation; } FCOORD classify_rotation() const { return classify_rotation_; // Apply this before classifying. } void set_classify_rotation(const FCOORD& rotation) { classify_rotation_ = rotation; } FCOORD skew() const { return skew_; // Direction of true horizontal. } void set_skew(const FCOORD& skew) { skew_ = skew; } const ICOORD& median_size() const { return median_size_; } void set_median_size(int x, int y) { median_size_.set_x(x); median_size_.set_y(y); } Pix* render_mask(TBOX* mask_box) { return PDBLK::render_mask(re_rotation_, mask_box); } // Returns the bounding box including the desired combination of upper and // lower noise/diacritic elements. TBOX restricted_bounding_box(bool upper_dots, bool lower_dots) const; // Reflects the polygon in the y-axis and recomputes the bounding_box. // Does nothing to any contained rows/words/blobs etc. void reflect_polygon_in_y_axis(); void rotate(const FCOORD& rotation); /// decreasing y order void sort_rows(); /// shrink white space void compress(); /// check proportional void check_pitch(); /// shrink white space and move by vector void compress(const ICOORD vec); /// dump whole table void print(FILE *fp, BOOL8 dump); BLOCK& operator=(const BLOCK & source); private: BOOL8 proportional; //< proportional bool right_to_left_; //< major script is right to left. inT8 kerning; //< inter blob gap inT16 spacing; //< inter word gap inT16 pitch; //< pitch of non-props inT16 font_class; //< correct font class inT32 xheight; //< height of chars float cell_over_xheight_; //< Ratio of cell height to xheight. STRING filename; //< name of block ROW_LIST rows; //< rows in block PARA_LIST paras_; //< paragraphs of block C_BLOB_LIST c_blobs; //< before textord C_BLOB_LIST rej_blobs; //< duff stuff FCOORD re_rotation_; //< How to transform coords back to image. FCOORD classify_rotation_; //< Apply this before classifying. FCOORD skew_; //< Direction of true horizontal. ICOORD median_size_; //< Median size of blobs. }; int decreasing_top_order(const void *row1, const void *row2); // A function to print segmentation stats for the given block list. void PrintSegmentationStats(BLOCK_LIST* block_list); // Extracts blobs fromo the given block list and adds them to the output list. // The block list must have been created by performing a page segmentation. void ExtractBlobsFromSegmentation(BLOCK_LIST* blocks, C_BLOB_LIST* output_blob_list); // Refreshes the words in the block_list by using blobs in the // new_blobs list. // Block list must have word segmentation in it. // It consumes the blobs provided in the new_blobs list. The blobs leftover in // the new_blobs list after the call weren't matched to any blobs of the words // in block list. // The output not_found_blobs is a list of blobs from the original segmentation // in the block_list for which no corresponding new blobs were found. void RefreshWordBlobsFromNewBlobs(BLOCK_LIST* block_list, C_BLOB_LIST* new_blobs, C_BLOB_LIST* not_found_blobs); #endif tesseract-3.04.01/ccstruct/ocrpara.cpp000066400000000000000000000066401266071204500176520ustar00rootroot00000000000000///////////////////////////////////////////////////////////////////// // File: ocrpara.h // Description: OCR Paragraph Output Type // Author: David Eger // Created: 2010-11-15 // // (C) Copyright 2010, 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 "ocrpara.h" #include "host.h" // For NearlyEqual() ELISTIZE(PARA) using tesseract::JUSTIFICATION_LEFT; using tesseract::JUSTIFICATION_RIGHT; using tesseract::JUSTIFICATION_CENTER; using tesseract::JUSTIFICATION_UNKNOWN; static STRING ParagraphJustificationToString( tesseract::ParagraphJustification justification) { switch (justification) { case JUSTIFICATION_LEFT: return "LEFT"; case JUSTIFICATION_RIGHT: return "RIGHT"; case JUSTIFICATION_CENTER: return "CENTER"; default: return "UNKNOWN"; } } bool ParagraphModel::ValidFirstLine(int lmargin, int lindent, int rindent, int rmargin) const { switch (justification_) { case JUSTIFICATION_LEFT: return NearlyEqual(lmargin + lindent, margin_ + first_indent_, tolerance_); case JUSTIFICATION_RIGHT: return NearlyEqual(rmargin + rindent, margin_ + first_indent_, tolerance_); case JUSTIFICATION_CENTER: return NearlyEqual(lindent, rindent, tolerance_ * 2); default: // shouldn't happen return false; } } bool ParagraphModel::ValidBodyLine(int lmargin, int lindent, int rindent, int rmargin) const { switch (justification_) { case JUSTIFICATION_LEFT: return NearlyEqual(lmargin + lindent, margin_ + body_indent_, tolerance_); case JUSTIFICATION_RIGHT: return NearlyEqual(rmargin + rindent, margin_ + body_indent_, tolerance_); case JUSTIFICATION_CENTER: return NearlyEqual(lindent, rindent, tolerance_ * 2); default: // shouldn't happen return false; } } bool ParagraphModel::Comparable(const ParagraphModel &other) const { if (justification_ != other.justification_) return false; if (justification_ == JUSTIFICATION_CENTER || justification_ == JUSTIFICATION_UNKNOWN) return true; int tolerance = (tolerance_ + other.tolerance_) / 4; return NearlyEqual(margin_ + first_indent_, other.margin_ + other.first_indent_, tolerance) && NearlyEqual(margin_ + body_indent_, other.margin_ + other.body_indent_, tolerance); } STRING ParagraphModel::ToString() const { char buffer[200]; const STRING &alignment = ParagraphJustificationToString(justification_); snprintf(buffer, sizeof(buffer), "margin: %d, first_indent: %d, body_indent: %d, alignment: %s", margin_, first_indent_, body_indent_, alignment.string()); return STRING(buffer); } tesseract-3.04.01/ccstruct/ocrpara.h000066400000000000000000000164001266071204500173120ustar00rootroot00000000000000///////////////////////////////////////////////////////////////////// // File: ocrpara.h // Description: OCR Paragraph Output Type // Author: David Eger // Created: 2010-11-15 // // (C) Copyright 2010, 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_OCRPARA_H_ #define TESSERACT_CCSTRUCT_OCRPARA_H_ #include "publictypes.h" #include "elst.h" #include "strngs.h" class ParagraphModel; struct PARA : public ELIST_LINK { public: PARA() : model(NULL), is_list_item(false), is_very_first_or_continuation(false), has_drop_cap(false) {} // We do not own the model, we just reference it. // model may be NULL if there is not a good model for this paragraph. const ParagraphModel *model; bool is_list_item; // The first paragraph on a page often lacks a first line indent, but should // still be modeled by the same model as other body text paragraphs on the // page. bool is_very_first_or_continuation; // Does this paragraph begin with a drop cap? bool has_drop_cap; }; ELISTIZEH(PARA) // A geometric model of paragraph indentation and alignment. // // Measurements are in pixels. The meaning of the integer arguments changes // depending upon the value of justification. Distances less than or equal // to tolerance apart we take as "equivalent" for the purpose of model // matching, and in the examples below, we assume tolerance is zero. // // justification = LEFT: // margin the "ignored" margin to the left block edge. // first_indent indent from the left margin to a typical first text line. // body_indent indent from the left margin of a typical body text line. // // justification = RIGHT: // margin the "ignored" margin to the right block edge. // first_indent indent from the right margin to a typical first text line. // body_indent indent from the right margin of a typical body text line. // // justification = CENTER: // margin ignored // first_indent ignored // body_indent ignored // // ====== Extended example, assuming each letter is ten pixels wide: ======= // // +--------------------------------+ // | Awesome | ParagraphModel(CENTER, 0, 0, 0) // | Centered Title | // | Paragraph Detection | // | OCR TEAM | // | 10 November 2010 | // | | // | Look here, I have a paragraph.| ParagraphModel(LEFT, 0, 20, 0) // |This paragraph starts at the top| // |of the page and takes 3 lines. | // | Here I have a second paragraph| ParagraphModel(LEFT, 0, 20, 0) // |which indicates that the first | // |paragraph is not a continuation | // |from a previous page, as it is | // |indented just like this second | // |paragraph. | // | Here is a block quote. It | ParagraphModel(LEFT, 30, 0, 0) // | looks like the prior text | // | but it is indented more | // | and is fully justified. | // | So how does one deal with | ParagraphModel(LEFT, 0, 20, 0) // |centered text, block quotes, | // |normal paragraphs, and lists | // |like what follows? | // |1. Make a plan. | ParagraphModel(LEFT, 0, 0, 30) // |2. Use a heuristic, for example,| ParagraphModel(LEFT, 0, 0, 30) // | looking for lines where the | // | first word of the next line | // | would fit on the previous | // | line. | // |8. Try to implement the plan in | ParagraphModel(LEFT, 0, 0, 30) // | Python and try it out. | // |4. Determine how to fix the | ParagraphModel(LEFT, 0, 0, 30) // | mistakes. | // |5. Repeat. | ParagraphModel(LEFT, 0, 0, 30) // | For extra painful penalty work| ParagraphModel(LEFT, 0, 20, 0) // |you can try to identify source | // |code. Ouch! | // +--------------------------------+ class ParagraphModel { public: ParagraphModel(tesseract::ParagraphJustification justification, int margin, int first_indent, int body_indent, int tolerance) : justification_(justification), margin_(margin), first_indent_(first_indent), body_indent_(body_indent), tolerance_(tolerance) { // Make one of {first_indent, body_indent} is 0. int added_margin = first_indent; if (body_indent < added_margin) added_margin = body_indent; margin_ += added_margin; first_indent_ -= added_margin; body_indent_ -= added_margin; } ParagraphModel() : justification_(tesseract::JUSTIFICATION_UNKNOWN), margin_(0), first_indent_(0), body_indent_(0), tolerance_(0) { } // ValidFirstLine() and ValidBodyLine() take arguments describing a text line // in a block of text which we are trying to model: // lmargin, lindent: these add up to the distance from the leftmost ink // in the text line to the surrounding text block's left // edge. // rmargin, rindent: these add up to the distance from the rightmost ink // in the text line to the surrounding text block's right // edge. // The caller determines the division between "margin" and "indent", which // only actually affect whether we think the line may be centered. // // If the amount of whitespace matches the amount of whitespace expected on // the relevant side of the line (within tolerance_) we say it matches. // Return whether a given text line could be a first paragraph line according // to this paragraph model. bool ValidFirstLine(int lmargin, int lindent, int rindent, int rmargin) const; // Return whether a given text line could be a first paragraph line according // to this paragraph model. bool ValidBodyLine(int lmargin, int lindent, int rindent, int rmargin) const; tesseract::ParagraphJustification justification() const { return justification_; } int margin() const { return margin_; } int first_indent() const { return first_indent_; } int body_indent() const { return body_indent_; } int tolerance() const { return tolerance_; } bool is_flush() const { return (justification_ == tesseract::JUSTIFICATION_LEFT || justification_ == tesseract::JUSTIFICATION_RIGHT) && abs(first_indent_ - body_indent_) <= tolerance_; } // Return whether this model is likely to agree with the other model on most // paragraphs they are marked. bool Comparable(const ParagraphModel &other) const; STRING ToString() const; private: tesseract::ParagraphJustification justification_; int margin_; int first_indent_; int body_indent_; int tolerance_; }; #endif // TESSERACT_CCSTRUCT_OCRPARA_H_ tesseract-3.04.01/ccstruct/ocrrow.cpp000066400000000000000000000171031266071204500175320ustar00rootroot00000000000000/********************************************************************** * File: ocrrow.cpp (Formerly row.c) * Description: Code for the ROW class. * Author: Ray Smith * Created: Tue Oct 08 15:58:04 BST 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include "ocrrow.h" #include "blobbox.h" // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif ELISTIZE (ROW) /********************************************************************** * ROW::ROW * * Constructor to build a ROW. Only the stats stuff are given here. * The words are added directly. **********************************************************************/ ROW::ROW ( //constructor inT32 spline_size, //no of segments inT32 * xstarts, //segment boundaries double *coeffs, //coefficients float x_height, //line height float ascenders, //ascender size float descenders, //descender drop inT16 kern, //char gap inT16 space //word gap ) : baseline(spline_size, xstarts, coeffs), para_(NULL) { kerning = kern; //just store stuff spacing = space; xheight = x_height; ascrise = ascenders; bodysize = 0.0f; descdrop = descenders; has_drop_cap_ = false; lmargin_ = 0; rmargin_ = 0; } /********************************************************************** * ROW::ROW * * Constructor to build a ROW. Only the stats stuff are given here. * The words are added directly. **********************************************************************/ ROW::ROW( //constructor TO_ROW *to_row, //source row inT16 kern, //char gap inT16 space //word gap ) : para_(NULL) { kerning = kern; //just store stuff spacing = space; xheight = to_row->xheight; bodysize = to_row->body_size; ascrise = to_row->ascrise; descdrop = to_row->descdrop; baseline = to_row->baseline; has_drop_cap_ = false; lmargin_ = 0; rmargin_ = 0; } // Returns the bounding box including the desired combination of upper and // lower noise/diacritic elements. TBOX ROW::restricted_bounding_box(bool upper_dots, bool lower_dots) const { TBOX box; // This is a read-only iteration of the words in the row. WERD_IT it(const_cast(&words)); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { box += it.data()->restricted_bounding_box(upper_dots, lower_dots); } return box; } /********************************************************************** * ROW::recalc_bounding_box * * Set the bounding box correctly **********************************************************************/ void ROW::recalc_bounding_box() { //recalculate BB WERD *word; //current word WERD_IT it = &words; //words of ROW inT16 left; //of word inT16 prev_left; //old left if (!it.empty ()) { word = it.data (); prev_left = word->bounding_box ().left (); it.forward (); while (!it.at_first ()) { word = it.data (); left = word->bounding_box ().left (); if (left < prev_left) { it.move_to_first (); //words in BB order it.sort (word_comparator); break; } prev_left = left; it.forward (); } } for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { word = it.data (); if (it.at_first ()) word->set_flag (W_BOL, TRUE); else //not start of line word->set_flag (W_BOL, FALSE); if (it.at_last ()) word->set_flag (W_EOL, TRUE); else //not end of line word->set_flag (W_EOL, FALSE); //extend BB as reqd bound_box += word->bounding_box (); } } /********************************************************************** * ROW::move * * Reposition row by vector **********************************************************************/ void ROW::move( // reposition row const ICOORD vec // by vector ) { WERD_IT it(&words); // word iterator for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) it.data ()->move (vec); bound_box.move (vec); baseline.move (vec); } /********************************************************************** * ROW::print * * Display members **********************************************************************/ void ROW::print( //print FILE *fp //file to print on ) { tprintf("Kerning= %d\n", kerning); tprintf("Spacing= %d\n", spacing); bound_box.print(); tprintf("Xheight= %f\n", xheight); tprintf("Ascrise= %f\n", ascrise); tprintf("Descdrop= %f\n", descdrop); tprintf("has_drop_cap= %d\n", has_drop_cap_); tprintf("lmargin= %d, rmargin= %d\n", lmargin_, rmargin_); } /********************************************************************** * ROW::plot * * Draw the ROW in the given colour. **********************************************************************/ #ifndef GRAPHICS_DISABLED void ROW::plot( //draw it ScrollView* window, //window to draw in ScrollView::Color colour //colour to draw in ) { WERD *word; //current word WERD_IT it = &words; //words of ROW for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { word = it.data (); word->plot (window, colour); //all in one colour } } /********************************************************************** * ROW::plot * * Draw the ROW in rainbow colours. **********************************************************************/ void ROW::plot( //draw it ScrollView* window //window to draw in ) { WERD *word; //current word WERD_IT it = &words; //words of ROW for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { word = it.data (); word->plot (window); //in rainbow colours } } #endif // GRAPHICS_DISABLED /********************************************************************** * ROW::operator= * * Assign rows by duplicating the row structure but NOT the WERDLIST **********************************************************************/ ROW & ROW::operator= (const ROW & source) { this->ELIST_LINK::operator= (source); kerning = source.kerning; spacing = source.spacing; xheight = source.xheight; bodysize = source.bodysize; ascrise = source.ascrise; descdrop = source.descdrop; if (!words.empty ()) words.clear (); baseline = source.baseline; //QSPLINES must do = bound_box = source.bound_box; has_drop_cap_ = source.has_drop_cap_; lmargin_ = source.lmargin_; rmargin_ = source.rmargin_; para_ = source.para_; return *this; } tesseract-3.04.01/ccstruct/ocrrow.h000066400000000000000000000120641266071204500172000ustar00rootroot00000000000000/********************************************************************** * File: ocrrow.h (Formerly row.h) * Description: Code for the ROW class. * Author: Ray Smith * Created: Tue Oct 08 15:58:04 BST 1991 * * (C) Copyright 1991, 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 OCRROW_H #define OCRROW_H #include #include "quspline.h" #include "werd.h" class TO_ROW; struct PARA; class ROW:public ELIST_LINK { friend void tweak_row_baseline(ROW *, double, double); public: ROW() { } //empty constructor ROW( //constructor inT32 spline_size, //no of segments inT32 *xstarts, //segment boundaries double *coeffs, //coefficients //ascender size float x_height, float ascenders, float descenders, //descender size inT16 kern, //char gap inT16 space); //word gap ROW( //constructor TO_ROW *row, //textord row inT16 kern, //char gap inT16 space); //word gap WERD_LIST *word_list() { //get words return &words; } float base_line( //compute baseline float xpos) const { //at the position //get spline value return (float) baseline.y (xpos); } float x_height() const { //return x height return xheight; } void set_x_height(float new_xheight) { // set x height xheight = new_xheight; } inT32 kern() const { //return kerning return kerning; } float body_size() const { //return body size return bodysize; } void set_body_size(float new_size) { // set body size bodysize = new_size; } inT32 space() const { //return spacing return spacing; } float ascenders() const { //return size return ascrise; } float descenders() const { //return size return descdrop; } TBOX bounding_box() const { //return bounding box return bound_box; } // Returns the bounding box including the desired combination of upper and // lower noise/diacritic elements. TBOX restricted_bounding_box(bool upper_dots, bool lower_dots) const; void set_lmargin(inT16 lmargin) { lmargin_ = lmargin; } void set_rmargin(inT16 rmargin) { rmargin_ = rmargin; } inT16 lmargin() const { return lmargin_; } inT16 rmargin() const { return rmargin_; } void set_has_drop_cap(bool has) { has_drop_cap_ = has; } bool has_drop_cap() const { return has_drop_cap_; } void set_para(PARA *p) { para_ = p; } PARA *para() const { return para_; } void recalc_bounding_box(); //recalculate BB void move( // reposition row const ICOORD vec); // by vector void print( //print FILE *fp); //file to print on #ifndef GRAPHICS_DISABLED void plot( //draw one ScrollView* window, //window to draw in ScrollView::Color colour); //uniform colour void plot( //draw one ScrollView* window); //in rainbow colours void plot_baseline( //draw the baseline ScrollView* window, //window to draw in ScrollView::Color colour) { //colour to draw //draw it baseline.plot (window, colour); } #endif // GRAPHICS_DISABLED ROW& operator= (const ROW & source); private: inT32 kerning; //inter char gap inT32 spacing; //inter word gap TBOX bound_box; //bounding box float xheight; //height of line float ascrise; //size of ascenders float descdrop; //-size of descenders float bodysize; //CJK character size. (equals to //xheight+ascrise by default) WERD_LIST words; //words QSPLINE baseline; //baseline spline // These get set after blocks have been determined. bool has_drop_cap_; inT16 lmargin_; // Distance to left polyblock margin. inT16 rmargin_; // Distance to right polyblock margin. // This gets set during paragraph analysis. PARA *para_; // Paragraph of which this row is part. }; ELISTIZEH (ROW) #endif tesseract-3.04.01/ccstruct/otsuthr.cpp000066400000000000000000000175231266071204500177350ustar00rootroot00000000000000/********************************************************************** * File: otsuthr.cpp * Description: Simple Otsu thresholding for binarizing images. * Author: Ray Smith * Created: Fri Mar 07 12:31:01 PST 2008 * * (C) Copyright 2008, 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 "otsuthr.h" #include #include "allheaders.h" #include "helpers.h" #include "openclwrapper.h" namespace tesseract { // Computes the Otsu threshold(s) for the given image rectangle, making one // for each channel. Each channel is always one byte per pixel. // Returns an array of threshold values and an array of hi_values, such // that a pixel value >threshold[channel] is considered foreground if // hi_values[channel] is 0 or background if 1. A hi_value of -1 indicates // that there is no apparent foreground. At least one hi_value will not be -1. // Delete thresholds and hi_values with delete [] after use. // The return value is the number of channels in the input image, being // the size of the output thresholds and hi_values arrays. int OtsuThreshold(Pix* src_pix, int left, int top, int width, int height, int** thresholds, int** hi_values) { int num_channels = pixGetDepth(src_pix) / 8; // Of all channels with no good hi_value, keep the best so we can always // produce at least one answer. PERF_COUNT_START("OtsuThreshold") int best_hi_value = 1; int best_hi_index = 0; bool any_good_hivalue = false; double best_hi_dist = 0.0; *thresholds = new int[num_channels]; *hi_values = new int[num_channels]; // all of channel 0 then all of channel 1... int *histogramAllChannels = new int[kHistogramSize * num_channels]; // only use opencl if compiled w/ OpenCL and selected device is opencl #ifdef USE_OPENCL // Calculate Histogram on GPU OpenclDevice od; if (od.selectedDeviceIsOpenCL() && (num_channels == 1 || num_channels == 4) && top == 0 && left == 0 ) { od.HistogramRectOCL( (const unsigned char*)pixGetData(src_pix), num_channels, pixGetWpl(src_pix) * 4, left, top, width, height, kHistogramSize, histogramAllChannels); // Calculate Threshold from Histogram on cpu for (int ch = 0; ch < num_channels; ++ch) { (*thresholds)[ch] = -1; (*hi_values)[ch] = -1; int *histogram = &histogramAllChannels[kHistogramSize * ch]; int H; int best_omega_0; int best_t = OtsuStats(histogram, &H, &best_omega_0); if (best_omega_0 == 0 || best_omega_0 == H) { // This channel is empty. continue; } // To be a convincing foreground we must have a small fraction of H // or to be a convincing background we must have a large fraction of H. // In between we assume this channel contains no thresholding information. int hi_value = best_omega_0 < H * 0.5; (*thresholds)[ch] = best_t; if (best_omega_0 > H * 0.75) { any_good_hivalue = true; (*hi_values)[ch] = 0; } else if (best_omega_0 < H * 0.25) { any_good_hivalue = true; (*hi_values)[ch] = 1; } else { // In case all channels are like this, keep the best of the bad lot. double hi_dist = hi_value ? (H - best_omega_0) : best_omega_0; if (hi_dist > best_hi_dist) { best_hi_dist = hi_dist; best_hi_value = hi_value; best_hi_index = ch; } } } } else { #endif for (int ch = 0; ch < num_channels; ++ch) { (*thresholds)[ch] = -1; (*hi_values)[ch] = -1; // Compute the histogram of the image rectangle. int histogram[kHistogramSize]; HistogramRect(src_pix, ch, left, top, width, height, histogram); int H; int best_omega_0; int best_t = OtsuStats(histogram, &H, &best_omega_0); if (best_omega_0 == 0 || best_omega_0 == H) { // This channel is empty. continue; } // To be a convincing foreground we must have a small fraction of H // or to be a convincing background we must have a large fraction of H. // In between we assume this channel contains no thresholding information. int hi_value = best_omega_0 < H * 0.5; (*thresholds)[ch] = best_t; if (best_omega_0 > H * 0.75) { any_good_hivalue = true; (*hi_values)[ch] = 0; } else if (best_omega_0 < H * 0.25) { any_good_hivalue = true; (*hi_values)[ch] = 1; } else { // In case all channels are like this, keep the best of the bad lot. double hi_dist = hi_value ? (H - best_omega_0) : best_omega_0; if (hi_dist > best_hi_dist) { best_hi_dist = hi_dist; best_hi_value = hi_value; best_hi_index = ch; } } } #ifdef USE_OPENCL } #endif // USE_OPENCL delete[] histogramAllChannels; if (!any_good_hivalue) { // Use the best of the ones that were not good enough. (*hi_values)[best_hi_index] = best_hi_value; } PERF_COUNT_END return num_channels; } // Computes the histogram for the given image rectangle, and the given // single channel. Each channel is always one byte per pixel. // Histogram is always a kHistogramSize(256) element array to count // occurrences of each pixel value. void HistogramRect(Pix* src_pix, int channel, int left, int top, int width, int height, int* histogram) { PERF_COUNT_START("HistogramRect") int num_channels = pixGetDepth(src_pix) / 8; channel = ClipToRange(channel, 0, num_channels - 1); int bottom = top + height; memset(histogram, 0, sizeof(*histogram) * kHistogramSize); int src_wpl = pixGetWpl(src_pix); l_uint32* srcdata = pixGetData(src_pix); for (int y = top; y < bottom; ++y) { const l_uint32* linedata = srcdata + y * src_wpl; for (int x = 0; x < width; ++x) { int pixel = GET_DATA_BYTE(const_cast( reinterpret_cast(linedata)), (x + left) * num_channels + channel); ++histogram[pixel]; } } PERF_COUNT_END } // Computes the Otsu threshold(s) for the given histogram. // Also returns H = total count in histogram, and // omega0 = count of histogram below threshold. int OtsuStats(const int* histogram, int* H_out, int* omega0_out) { int H = 0; double mu_T = 0.0; for (int i = 0; i < kHistogramSize; ++i) { H += histogram[i]; mu_T += static_cast(i) * histogram[i]; } // Now maximize sig_sq_B over t. // http://www.ctie.monash.edu.au/hargreave/Cornall_Terry_328.pdf int best_t = -1; int omega_0, omega_1; int best_omega_0 = 0; double best_sig_sq_B = 0.0; double mu_0, mu_1, mu_t; omega_0 = 0; mu_t = 0.0; for (int t = 0; t < kHistogramSize - 1; ++t) { omega_0 += histogram[t]; mu_t += t * static_cast(histogram[t]); if (omega_0 == 0) continue; omega_1 = H - omega_0; if (omega_1 == 0) break; mu_0 = mu_t / omega_0; mu_1 = (mu_T - mu_t) / omega_1; double sig_sq_B = mu_1 - mu_0; sig_sq_B *= sig_sq_B * omega_0 * omega_1; if (best_t < 0 || sig_sq_B > best_sig_sq_B) { best_sig_sq_B = sig_sq_B; best_t = t; best_omega_0 = omega_0; } } if (H_out != NULL) *H_out = H; if (omega0_out != NULL) *omega0_out = best_omega_0; return best_t; } } // namespace tesseract. tesseract-3.04.01/ccstruct/otsuthr.h000066400000000000000000000047161266071204500174020ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: otsuthr.h // Description: Simple Otsu thresholding for binarizing images. // Author: Ray Smith // Created: Fri Mar 07 12:14:01 PST 2008 // // (C) Copyright 2008, 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_CCMAIN_OTSUTHR_H__ #define TESSERACT_CCMAIN_OTSUTHR_H__ struct Pix; namespace tesseract { const int kHistogramSize = 256; // The size of a histogram of pixel values. // Computes the Otsu threshold(s) for the given image rectangle, making one // for each channel. Each channel is always one byte per pixel. // Returns an array of threshold values and an array of hi_values, such // that a pixel value >threshold[channel] is considered foreground if // hi_values[channel] is 0 or background if 1. A hi_value of -1 indicates // that there is no apparent foreground. At least one hi_value will not be -1. // Delete thresholds and hi_values with delete [] after use. // The return value is the number of channels in the input image, being // the size of the output thresholds and hi_values arrays. int OtsuThreshold(Pix* src_pix, int left, int top, int width, int height, int** thresholds, int** hi_values); // Computes the histogram for the given image rectangle, and the given // single channel. Each channel is always one byte per pixel. // Histogram is always a kHistogramSize(256) element array to count // occurrences of each pixel value. void HistogramRect(Pix* src_pix, int channel, int left, int top, int width, int height, int* histogram); // Computes the Otsu threshold(s) for the given histogram. // Also returns H = total count in histogram, and // omega0 = count of histogram below threshold. int OtsuStats(const int* histogram, int* H_out, int* omega0_out); } // namespace tesseract. #endif // TESSERACT_CCMAIN_OTSUTHR_H__ tesseract-3.04.01/ccstruct/pageres.cpp000066400000000000000000001700431266071204500176500ustar00rootroot00000000000000/********************************************************************** * File: pageres.cpp (Formerly page_res.c) * Description: Hierarchy of results classes from PAGE_RES to WERD_RES * and an iterator class to iterate over the words. * Main purposes: * Easy way to iterate over the words without a 3-nested loop. * Holds data used during word recognition. * Holds information about alternative spacing paths. * Author: Phil Cheatle * Created: Tue Sep 22 08:42:49 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. * **********************************************************************/ #include #ifdef __UNIX__ #include #endif #include "blamer.h" #include "pageres.h" #include "blobs.h" 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; // Max number of broken pieces to associate. const int kWordrecMaxNumJoinChunks = 4; // Max ratio of word box height to line size to allow it to be processed as // a line with other words. const double kMaxWordSizeRatio = 1.25; // Max ratio of line box height to line size to allow a new word to be added. const double kMaxLineSizeRatio = 1.25; // Max ratio of word gap to line size to allow a new word to be added. const double kMaxWordGapRatio = 2.0; // 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 * * Constructor for page results *************************************************************************/ PAGE_RES::PAGE_RES( bool merge_similar_words, BLOCK_LIST *the_block_list, WERD_CHOICE **prev_word_best_choice_ptr) { Init(); BLOCK_IT block_it(the_block_list); BLOCK_RES_IT block_res_it(&block_res_list); for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) { block_res_it.add_to_end(new BLOCK_RES(merge_similar_words, block_it.data())); } prev_word_best_choice = prev_word_best_choice_ptr; } /************************************************************************* * BLOCK_RES::BLOCK_RES * * Constructor for BLOCK results *************************************************************************/ BLOCK_RES::BLOCK_RES(bool merge_similar_words, BLOCK *the_block) { ROW_IT row_it (the_block->row_list ()); ROW_RES_IT row_res_it(&row_res_list); char_count = 0; rej_count = 0; font_class = -1; //not assigned x_height = -1.0; font_assigned = FALSE; bold = FALSE; italic = FALSE; row_count = 0; block = the_block; for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { row_res_it.add_to_end(new ROW_RES(merge_similar_words, row_it.data())); } } /************************************************************************* * ROW_RES::ROW_RES * * Constructor for ROW results *************************************************************************/ ROW_RES::ROW_RES(bool merge_similar_words, ROW *the_row) { WERD_IT word_it(the_row->word_list()); WERD_RES_IT word_res_it(&word_res_list); WERD_RES *combo = NULL; // current combination of fuzzies WERD *copy_word; char_count = 0; rej_count = 0; whole_word_rej_count = 0; row = the_row; bool add_next_word = false; TBOX union_box; float line_height = the_row->x_height() + the_row->ascenders() - the_row->descenders(); for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) { WERD_RES* word_res = new WERD_RES(word_it.data()); word_res->x_height = the_row->x_height(); if (add_next_word) { ASSERT_HOST(combo != NULL); // We are adding this word to the combination. word_res->part_of_combo = TRUE; combo->copy_on(word_res); } else if (merge_similar_words) { union_box = word_res->word->bounding_box(); add_next_word = !word_res->word->flag(W_REP_CHAR) && union_box.height() <= line_height * kMaxWordSizeRatio; word_res->odd_size = !add_next_word; } WERD* next_word = word_it.data_relative(1); if (merge_similar_words) { if (add_next_word && !next_word->flag(W_REP_CHAR)) { // Next word will be added on if all of the following are true: // Not a rep char. // Box height small enough. // Union box height small enough. // Horizontal gap small enough. TBOX next_box = next_word->bounding_box(); int prev_right = union_box.right(); union_box += next_box; if (next_box.height() > line_height * kMaxWordSizeRatio || union_box.height() > line_height * kMaxLineSizeRatio || next_box.left() > prev_right + line_height * kMaxWordGapRatio) { add_next_word = false; } } next_word->set_flag(W_FUZZY_NON, add_next_word); } else { add_next_word = next_word->flag(W_FUZZY_NON); } if (add_next_word) { if (combo == NULL) { copy_word = new WERD; *copy_word = *(word_it.data()); // deep copy combo = new WERD_RES(copy_word); combo->x_height = the_row->x_height(); combo->combination = TRUE; word_res_it.add_to_end(combo); } word_res->part_of_combo = TRUE; } else { combo = NULL; } word_res_it.add_to_end(word_res); } } WERD_RES& WERD_RES::operator=(const WERD_RES & source) { this->ELIST_LINK::operator=(source); Clear(); if (source.combination) { word = new WERD; *word = *(source.word); // deep copy } else { word = source.word; // pt to same word } if (source.bln_boxes != NULL) bln_boxes = new tesseract::BoxWord(*source.bln_boxes); if (source.chopped_word != NULL) chopped_word = new TWERD(*source.chopped_word); 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; 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)); } if (!wc_dest_it.empty()) { wc_dest_it.move_to_first(); best_choice = wc_dest_it.data(); } else { best_choice = NULL; } if (source.raw_choice != NULL) { raw_choice = new WERD_CHOICE(*source.raw_choice); } else { raw_choice = NULL; } if (source.ep_choice != NULL) { ep_choice = new WERD_CHOICE(*source.ep_choice); } else { ep_choice = NULL; } reject_map = source.reject_map; combination = source.combination; part_of_combo = source.part_of_combo; CopySimpleFields(source); if (source.blamer_bundle != NULL) { blamer_bundle = new BlamerBundle(*(source.blamer_bundle)); } return *this; } // Copies basic fields that don't involve pointers that might be useful // to copy when making one WERD_RES from another. void WERD_RES::CopySimpleFields(const WERD_RES& source) { tess_failed = source.tess_failed; tess_accepted = source.tess_accepted; tess_would_adapt = source.tess_would_adapt; done = source.done; unlv_crunch_mode = source.unlv_crunch_mode; small_caps = source.small_caps; odd_size = source.odd_size; italic = source.italic; bold = source.bold; fontinfo = source.fontinfo; fontinfo2 = source.fontinfo2; fontinfo_id_count = source.fontinfo_id_count; fontinfo_id2_count = source.fontinfo_id2_count; x_height = source.x_height; caps_height = source.caps_height; baseline_shift = source.baseline_shift; guessed_x_ht = source.guessed_x_ht; guessed_caps_ht = source.guessed_caps_ht; reject_spaces = source.reject_spaces; uch_set = source.uch_set; tesseract = source.tesseract; } // Initializes a blank (default constructed) WERD_RES from one that has // already been recognized. // Use SetupFor*Recognition afterwards to complete the setup and make // it ready for a retry recognition. void WERD_RES::InitForRetryRecognition(const WERD_RES& source) { word = source.word; CopySimpleFields(source); if (source.blamer_bundle != NULL) { blamer_bundle = new BlamerBundle(); blamer_bundle->CopyTruth(*source.blamer_bundle); } } // 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. // The norm_mode_hint sets the default mode for normalization in absence // of any of the above flags. // norm_box is used to override the word bounding box to determine the // normalization scale and offset. // Returns false if the word is empty and sets up fake results. bool WERD_RES::SetupForRecognition(const UNICHARSET& unicharset_in, tesseract::Tesseract* tess, Pix* pix, int norm_mode, const TBOX* norm_box, bool numeric_mode, bool use_body_size, bool allow_detailed_fx, ROW *row, const BLOCK* block) { tesseract::OcrEngineMode norm_mode_hint = static_cast(norm_mode); tesseract = tess; POLY_BLOCK* pb = block != NULL ? block->poly_block() : NULL; if ((norm_mode_hint != tesseract::OEM_CUBE_ONLY && word->cblob_list()->empty()) || (pb != NULL && !pb->IsText())) { // Empty words occur when all the blobs have been moved to the rej_blobs // list, which seems to occur frequently in junk. SetupFake(unicharset_in); word->set_flag(W_REP_CHAR, false); return false; } ClearResults(); SetupWordScript(unicharset_in); chopped_word = TWERD::PolygonalCopy(allow_detailed_fx, word); float word_xheight = use_body_size && row != NULL && row->body_size() > 0.0f ? row->body_size() : x_height; chopped_word->BLNormalize(block, row, pix, word->flag(W_INVERSE), word_xheight, baseline_shift, numeric_mode, norm_mode_hint, norm_box, &denorm); blob_row = row; SetupBasicsFromChoppedWord(unicharset_in); SetupBlamerBundle(); int num_blobs = chopped_word->NumBlobs(); ratings = new MATRIX(num_blobs, kWordrecMaxNumJoinChunks); tess_failed = false; 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 for an empty recognition result: // bln_boxes, chopped_word, seam_array, denorm, best_choice, raw_choice. void WERD_RES::SetupFake(const UNICHARSET& unicharset_in) { ClearResults(); SetupWordScript(unicharset_in); chopped_word = new TWERD; rebuild_word = new TWERD; bln_boxes = new tesseract::BoxWord; box_word = new tesseract::BoxWord; int blob_count = word->cblob_list()->length(); 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 // and call the word failed with a fake classification. C_BLOB_IT b_it(word->cblob_list()); int blob_id = 0; for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { TBOX box = b_it.data()->bounding_box(); box_word->InsertBox(box_word->length(), box); fake_choices[blob_id++] = new BLOB_CHOICE; } 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; done = true; } void WERD_RES::SetupWordScript(const UNICHARSET& uch) { uch_set = &uch; int script = uch.default_sid(); word->set_script_id(script); word->set_flag(W_SCRIPT_HAS_XHEIGHT, uch.script_has_xheight()); word->set_flag(W_SCRIPT_IS_LATIN, script == uch.latin_sid()); } // Sets up the blamer_bundle if it is not null, using the initialized denorm. void WERD_RES::SetupBlamerBundle() { if (blamer_bundle != NULL) { 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. seam->PrepareToInsertSeam(seam_array, chopped_word->blobs, blob_number, true); seam_array.insert(seam, blob_number); 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()); } } } // Prints the top choice along with the accepted/done flags. void WERD_RES::DebugTopChoice(const char* msg) const { tprintf("Best choice: accepted=%d, adaptable=%d, done=%d : ", tess_accepted, tess_would_adapt, done); if (best_choice == NULL) tprintf("\n"); else best_choice->print(msg); } // 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) { delete *dest; *dest = *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); seam_array.delete_data_pointers(); seam_array = word->seam_array; word->seam_array.clear(); best_state.move(&word->best_state); correct_text.move(&word->correct_text); 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); 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); blamer_bundle->CopyResults(*(word->blamer_bundle)); } CopySimpleFields(*word); } // Replace the best choice and rebuild box word. // 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(best_state.length()); done = tess_accepted = tess_would_adapt = true; SetScriptPositions(); } // 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.empty()) start_seam_list(chopped_word, &seam_array); best_state.truncate(0); int start = 0; for (int i = 0; i < best_choice->length(); ++i) { int length = best_choice->state(i); best_state.push_back(length); if (length > 1) { SEAM::JoinPieces(seam_array, chopped_word->blobs, start, start + length - 1); } TBLOB* blob = chopped_word->blobs[start]; rebuild_word->blobs.push_back(new TBLOB(*blob)); if (length > 1) { SEAM::BreakPieces(seam_array, chopped_word->blobs, start, start + length - 1); } start += length; } } // Copies the chopped_word to the rebuild_word, faking a best_state as well. // Also sets up the output box_word. void WERD_RES::CloneChoppedToRebuild() { if (rebuild_word != NULL) delete rebuild_word; rebuild_word = new TWERD(*chopped_word); SetupBoxWord(); int word_len = box_word->length(); best_state.reserve(word_len); correct_text.reserve(word_len); for (int i = 0; i < word_len; ++i) { best_state.push_back(1); correct_text.push_back(STRING("")); } } // Sets/replaces the box_word with one made from the rebuild_word. void WERD_RES::SetupBoxWord() { if (box_word != NULL) delete box_word; rebuild_word->ComputeBoundingBoxes(); box_word = tesseract::BoxWord::CopyFromNormalized(rebuild_word); box_word->ClipToOriginalWord(denorm.block(), word); } // 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() { best_choice->SetScriptPositions(small_caps, chopped_word); } // 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 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()); ClearWordChoices(); ClearRatings(); ratings = new MATRIX(blob_count, 1); for (int c = 0; c < blob_count; ++c) { BLOB_CHOICE_LIST* choice_list = new BLOB_CHOICE_LIST; BLOB_CHOICE_IT choice_it(choice_list); choice_it.add_after_then_move(choices[c]); ratings->put(c, c, choice_list); } FakeWordFromRatings(); reject_map.initialise(blob_count); done = true; } // 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(); ASSERT_HOST(best_choice != NULL); for (int i = 0; i < best_choice->length(); ++i) { UNICHAR_ID choice_id = best_choice->unichar_id(i); const char* blob_choice = uch_set->id_to_unichar(choice_id); correct_text.push_back(STRING(blob_choice)); } } // Merges 2 adjacent blobs in the result if the permanent callback // class_cb returns other than INVALID_UNICHAR_ID, AND the permanent // callback box_cb is NULL or returns true, setting the merged blob // result to the class returned from class_cb. // Returns true if anything was merged. bool WERD_RES::ConditionalBlobMerge( TessResultCallback2* class_cb, 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), best_choice->unichar_id(i+1)); if (new_id != INVALID_UNICHAR_ID && (box_cb == NULL || box_cb->Run(box_word->BlobBox(i), box_word->BlobBox(i + 1)))) { // Raw choice should not be fixed. best_choice->set_unichar_id(new_id, i); modified = true; MergeAdjacentBlobs(i); const MATRIX_COORD& coord = best_choice->MatrixCoord(i); if (!coord.Valid(*ratings)) { ratings->IncreaseBandSize(coord.row + 1 - coord.col); } 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 class_cb; delete box_cb; 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. // Utility function for fix_quotes // Return true if the next character in the string (given the UTF8 length in // bytes) is a quote character. static int is_simple_quote(const char* signed_str, int length) { const unsigned char* str = reinterpret_cast(signed_str); // Standard 1 byte quotes. return (length == 1 && (*str == '\'' || *str == '`')) || // UTF-8 3 bytes curved quotes. (length == 3 && ((*str == 0xe2 && *(str + 1) == 0x80 && *(str + 2) == 0x98) || (*str == 0xe2 && *(str + 1) == 0x80 && *(str + 2) == 0x99))); } // Callback helper for fix_quotes returns a double quote if both // arguments are quote, otherwise INVALID_UNICHAR_ID. UNICHAR_ID WERD_RES::BothQuotes(UNICHAR_ID id1, UNICHAR_ID id2) { const char *ch = uch_set->id_to_unichar(id1); const char *next_ch = uch_set->id_to_unichar(id2); if (is_simple_quote(ch, strlen(ch)) && is_simple_quote(next_ch, strlen(next_ch))) return uch_set->unichar_to_id("\""); return INVALID_UNICHAR_ID; } // Change pairs of quotes to double quotes. 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); } // Callback helper for fix_hyphens returns UNICHAR_ID of - if both // arguments are hyphen, otherwise INVALID_UNICHAR_ID. UNICHAR_ID WERD_RES::BothHyphens(UNICHAR_ID id1, UNICHAR_ID id2) { const char *ch = uch_set->id_to_unichar(id1); const char *next_ch = uch_set->id_to_unichar(id2); if (strlen(ch) == 1 && strlen(next_ch) == 1 && (*ch == '-' || *ch == '~') && (*next_ch == '-' || *next_ch == '~')) return uch_set->unichar_to_id("-"); return INVALID_UNICHAR_ID; } // 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 WERD_RES::HyphenBoxesOverlap(const TBOX& box1, const TBOX& box2) { return box1.right() >= box2.left(); } // 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() { 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)); } // Callback helper for merge_tess_fails returns a space if both // arguments are space, otherwise INVALID_UNICHAR_ID. UNICHAR_ID WERD_RES::BothSpaces(UNICHAR_ID id1, UNICHAR_ID id2) { if (id1 == id2 && id1 == uch_set->unichar_to_id(" ")) return id1; else return INVALID_UNICHAR_ID; } // Change pairs of tess failures to a single one void WERD_RES::merge_tess_fails() { if (ConditionalBlobMerge( NewPermanentTessCallback(this, &WERD_RES::BothSpaces), NULL)) { int len = best_choice->length(); ASSERT_HOST(reject_map.length() == len); ASSERT_HOST(box_word->length() == len); } } // Returns true if the collection of count pieces, starting at start, are all // natural connected components, ie there are no real chops involved. 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 < seam_array.size()) { SEAM* seam = seam_array[index]; if (seam != NULL && seam->HasAnySplits()) return false; } } return true; } WERD_RES::~WERD_RES () { Clear(); } void WERD_RES::InitNonPointers() { tess_failed = FALSE; tess_accepted = FALSE; tess_would_adapt = FALSE; done = FALSE; unlv_crunch_mode = CR_NONE; small_caps = false; odd_size = false; italic = FALSE; bold = FALSE; // The fontinfos and tesseract count as non-pointers as they point to // data owned elsewhere. fontinfo = NULL; fontinfo2 = NULL; tesseract = NULL; fontinfo_id_count = 0; fontinfo_id2_count = 0; x_height = 0.0; caps_height = 0.0; baseline_shift = 0.0f; guessed_x_ht = TRUE; guessed_caps_ht = TRUE; combination = FALSE; part_of_combo = FALSE; reject_spaces = FALSE; } void WERD_RES::InitPointers() { word = NULL; bln_boxes = NULL; blob_row = NULL; uch_set = NULL; chopped_word = NULL; rebuild_word = NULL; box_word = NULL; ratings = NULL; best_choice = NULL; raw_choice = NULL; ep_choice = NULL; blamer_bundle = NULL; } void WERD_RES::Clear() { if (word != NULL && combination) { delete word; } word = NULL; delete blamer_bundle; blamer_bundle = NULL; ClearResults(); } void WERD_RES::ClearResults() { done = false; fontinfo = NULL; fontinfo2 = NULL; fontinfo_id_count = 0; fontinfo_id2_count = 0; if (bln_boxes != NULL) { delete bln_boxes; bln_boxes = NULL; } blob_row = NULL; if (chopped_word != NULL) { delete chopped_word; chopped_word = NULL; } if (rebuild_word != NULL) { delete rebuild_word; rebuild_word = NULL; } if (box_word != NULL) { delete box_word; box_word = NULL; } best_state.clear(); correct_text.clear(); 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; raw_choice = NULL; } best_choices.clear(); if (ep_choice != NULL) { delete ep_choice; ep_choice = NULL; } } 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 && row_res == other.row_res && block_res == other.block_res; } int PAGE_RES_IT::cmp(const PAGE_RES_IT &other) const { ASSERT_HOST(page_res == other.page_res); if (other.block_res == NULL) { // other points to the end of the page. if (block_res == NULL) return 0; return -1; } if (block_res == NULL) { return 1; // we point to the end of the page. } if (block_res == other.block_res) { if (other.row_res == NULL || row_res == NULL) { // this should only happen if we hit an image block. return 0; } if (row_res == other.row_res) { // we point to the same block and row. ASSERT_HOST(other.word_res != NULL && word_res != NULL); if (word_res == other.word_res) { // we point to the same word! return 0; } WERD_RES_IT word_res_it(&row_res->word_res_list); for (word_res_it.mark_cycle_pt(); !word_res_it.cycled_list(); word_res_it.forward()) { if (word_res_it.data() == word_res) { return -1; } else if (word_res_it.data() == other.word_res) { return 1; } } ASSERT_HOST("Error: Incomparable PAGE_RES_ITs" == NULL); } // we both point to the same block, but different rows. ROW_RES_IT row_res_it(&block_res->row_res_list); for (row_res_it.mark_cycle_pt(); !row_res_it.cycled_list(); row_res_it.forward()) { if (row_res_it.data() == row_res) { return -1; } else if (row_res_it.data() == other.row_res) { return 1; } } ASSERT_HOST("Error: Incomparable PAGE_RES_ITs" == NULL); } // We point to different blocks. BLOCK_RES_IT block_res_it(&page_res->block_res_list); for (block_res_it.mark_cycle_pt(); !block_res_it.cycled_list(); block_res_it.forward()) { if (block_res_it.data() == block_res) { return -1; } else if (block_res_it.data() == other.block_res) { return 1; } } // Shouldn't happen... ASSERT_HOST("Error: Incomparable PAGE_RES_ITs" == NULL); return 0; } // Inserts the new_word as a combination owned by a corresponding WERD_RES // before the current position. The simple fields of the WERD_RES are copied // from clone_res and the resulting WERD_RES is returned for further setup // with best_choice etc. WERD_RES* PAGE_RES_IT::InsertSimpleCloneWord(const WERD_RES& clone_res, WERD* new_word) { // Make a WERD_RES for the new_word. WERD_RES* new_res = new WERD_RES(new_word); new_res->CopySimpleFields(clone_res); new_res->combination = true; // Insert into the appropriate place in the ROW_RES. WERD_RES_IT wr_it(&row()->word_res_list); for (wr_it.mark_cycle_pt(); !wr_it.cycled_list(); wr_it.forward()) { WERD_RES* word = wr_it.data(); if (word == word_res) break; } ASSERT_HOST(!wr_it.cycled_list()); wr_it.add_before_then_move(new_res); if (wr_it.at_first()) { // This is the new first word, so reset the member iterator so it // detects the cycled_list state correctly. ResetWordIterator(); } return new_res; } // Helper computes the boundaries between blobs in the word. The blob bounds // are likely very poor, if they come from LSTM, where it only outputs the // character at one pixel within it, so we find the midpoints between them. static void ComputeBlobEnds(const WERD_RES& word, C_BLOB_LIST* next_word_blobs, GenericVector* blob_ends) { C_BLOB_IT blob_it(word.word->cblob_list()); for (int i = 0; i < word.best_state.size(); ++i) { int length = word.best_state[i]; // Get the bounding box of the fake blobs TBOX blob_box = blob_it.data()->bounding_box(); blob_it.forward(); for (int b = 1; b < length; ++b) { blob_box += blob_it.data()->bounding_box(); blob_it.forward(); } // This blob_box is crap, so for now we are only looking for the // boundaries between them. int blob_end = MAX_INT32; if (!blob_it.at_first() || next_word_blobs != NULL) { if (blob_it.at_first()) blob_it.set_to_list(next_word_blobs); blob_end = (blob_box.right() + blob_it.data()->bounding_box().left()) / 2; } blob_ends->push_back(blob_end); } } // Replaces the current WERD/WERD_RES with the given words. The given words // contain fake blobs that indicate the position of the characters. These are // replaced with real blobs from the current word as much as possible. void PAGE_RES_IT::ReplaceCurrentWord( tesseract::PointerVector* words) { if (words->empty()) { DeleteCurrentWord(); return; } WERD_RES* input_word = word(); // Set the BOL/EOL flags on the words from the input word. if (input_word->word->flag(W_BOL)) { (*words)[0]->word->set_flag(W_BOL, true); } else { (*words)[0]->word->set_blanks(1); } words->back()->word->set_flag(W_EOL, input_word->word->flag(W_EOL)); // Move the blobs from the input word to the new set of words. // If the input word_res is a combination, then the replacements will also be // combinations, and will own their own words. If the input word_res is not a // combination, then the final replacements will not be either, (although it // is allowed for the input words to be combinations) and their words // will get put on the row list. This maintains the ownership rules. WERD_IT w_it(row()->row->word_list()); if (!input_word->combination) { for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) { WERD* word = w_it.data(); if (word == input_word->word) break; } // w_it is now set to the input_word's word. ASSERT_HOST(!w_it.cycled_list()); } // Insert into the appropriate place in the ROW_RES. WERD_RES_IT wr_it(&row()->word_res_list); for (wr_it.mark_cycle_pt(); !wr_it.cycled_list(); wr_it.forward()) { WERD_RES* word = wr_it.data(); if (word == input_word) break; } ASSERT_HOST(!wr_it.cycled_list()); // Since we only have an estimate of the bounds between blobs, use the blob // x-middle as the determiner of where to put the blobs C_BLOB_IT src_b_it(input_word->word->cblob_list()); src_b_it.sort(&C_BLOB::SortByXMiddle); C_BLOB_IT rej_b_it(input_word->word->rej_cblob_list()); rej_b_it.sort(&C_BLOB::SortByXMiddle); for (int w = 0; w < words->size(); ++w) { WERD_RES* word_w = (*words)[w]; // Compute blob boundaries. GenericVector blob_ends; C_BLOB_LIST* next_word_blobs = w + 1 < words->size() ? (*words)[w + 1]->word->cblob_list() : NULL; ComputeBlobEnds(*word_w, next_word_blobs, &blob_ends); // Delete the fake blobs on the current word. word_w->word->cblob_list()->clear(); C_BLOB_IT dest_it(word_w->word->cblob_list()); // Build the box word as we move the blobs. tesseract::BoxWord* box_word = new tesseract::BoxWord; for (int i = 0; i < blob_ends.size(); ++i) { int end_x = blob_ends[i]; TBOX blob_box; // Add the blobs up to end_x. while (!src_b_it.empty() && src_b_it.data()->bounding_box().x_middle() < end_x) { blob_box += src_b_it.data()->bounding_box(); dest_it.add_after_then_move(src_b_it.extract()); src_b_it.forward(); } while (!rej_b_it.empty() && rej_b_it.data()->bounding_box().x_middle() < end_x) { blob_box += rej_b_it.data()->bounding_box(); dest_it.add_after_then_move(rej_b_it.extract()); rej_b_it.forward(); } // Clip to the previously computed bounds. Although imperfectly accurate, // it is good enough, and much more complicated to determine where else // to clip. if (i > 0 && blob_box.left() < blob_ends[i - 1]) blob_box.set_left(blob_ends[i - 1]); if (blob_box.right() > end_x) blob_box.set_right(end_x); box_word->InsertBox(i, blob_box); } // Fix empty boxes. If a very joined blob sits over multiple characters, // then we will have some empty boxes from using the middle, so look for // overlaps. for (int i = 0; i < box_word->length(); ++i) { TBOX box = box_word->BlobBox(i); if (box.null_box()) { // Nothing has its middle in the bounds of this blob, so use anything // that overlaps. for (dest_it.mark_cycle_pt(); !dest_it.cycled_list(); dest_it.forward()) { TBOX blob_box = dest_it.data()->bounding_box(); if (blob_box.left() < blob_ends[i] && (i == 0 || blob_box.right() >= blob_ends[i - 1])) { if (i > 0 && blob_box.left() < blob_ends[i - 1]) blob_box.set_left(blob_ends[i - 1]); if (blob_box.right() > blob_ends[i]) blob_box.set_right(blob_ends[i]); box_word->ChangeBox(i, blob_box); break; } } } } delete word_w->box_word; word_w->box_word = box_word; if (!input_word->combination) { // Insert word_w->word into the ROW. It doesn't own its word, so the // ROW needs to own it. w_it.add_before_stay_put(word_w->word); word_w->combination = false; } (*words)[w] = NULL; // We are taking ownership. wr_it.add_before_stay_put(word_w); } // We have taken ownership of the words. words->clear(); // Delete the current word, which has been replaced. We could just call // DeleteCurrentWord, but that would iterate both lists again, and we know // we are already in the right place. if (!input_word->combination) delete w_it.extract(); delete wr_it.extract(); ResetWordIterator(); } // Deletes the current WERD_RES and its underlying WERD. void PAGE_RES_IT::DeleteCurrentWord() { // Check that this word is as we expect. part_of_combos are NEVER iterated // by the normal iterator, so we should never be trying to delete them. ASSERT_HOST(!word_res->part_of_combo); if (!word_res->combination) { // Combinations own their own word, so we won't find the word on the // row's word_list, but it is legitimate to try to delete them. // Delete word from the ROW when not a combination. WERD_IT w_it(row()->row->word_list()); for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) { if (w_it.data() == word_res->word) { break; } } ASSERT_HOST(!w_it.cycled_list()); delete w_it.extract(); } // Remove the WERD_RES for the new_word. // Remove the WORD_RES from the ROW_RES. WERD_RES_IT wr_it(&row()->word_res_list); for (wr_it.mark_cycle_pt(); !wr_it.cycled_list(); wr_it.forward()) { if (wr_it.data() == word_res) { word_res = NULL; break; } } ASSERT_HOST(!wr_it.cycled_list()); delete wr_it.extract(); ResetWordIterator(); } // Makes the current word a fuzzy space if not already fuzzy. Updates // corresponding part of combo if required. void PAGE_RES_IT::MakeCurrentWordFuzzy() { WERD* real_word = word_res->word; if (!real_word->flag(W_FUZZY_SP) && !real_word->flag(W_FUZZY_NON)) { real_word->set_flag(W_FUZZY_SP, true); if (word_res->combination) { // The next word should be the corresponding part of combo, but we have // already stepped past it, so find it by search. WERD_RES_IT wr_it(&row()->word_res_list); for (wr_it.mark_cycle_pt(); !wr_it.cycled_list() && wr_it.data() != word_res; wr_it.forward()) { } wr_it.forward(); ASSERT_HOST(wr_it.data()->part_of_combo); real_word = wr_it.data()->word; ASSERT_HOST(!real_word->flag(W_FUZZY_SP) && !real_word->flag(W_FUZZY_NON)); real_word->set_flag(W_FUZZY_SP, true); } } } /************************************************************************* * PAGE_RES_IT::restart_page * * Set things up at the start of the page *************************************************************************/ WERD_RES *PAGE_RES_IT::start_page(bool empty_ok) { block_res_it.set_to_list(&page_res->block_res_list); block_res_it.mark_cycle_pt(); prev_block_res = NULL; prev_row_res = NULL; prev_word_res = NULL; block_res = NULL; row_res = NULL; word_res = NULL; next_block_res = NULL; next_row_res = NULL; next_word_res = NULL; internal_forward(true, empty_ok); return internal_forward(false, empty_ok); } // Recovers from operations on the current word, such as in InsertCloneWord // and DeleteCurrentWord. // Resets the word_res_it so that it is one past the next_word_res, as // it should be after internal_forward. If next_row_res != row_res, // then the next_word_res is in the next row, so there is no need to do // anything to word_res_it, but it is still a good idea to reset the pointers // word_res and prev_word_res, which are still in the current row. void PAGE_RES_IT::ResetWordIterator() { if (row_res == next_row_res) { // Reset the member iterator so it can move forward and detect the // cycled_list state correctly. word_res_it.move_to_first(); for (word_res_it.mark_cycle_pt(); !word_res_it.cycled_list() && word_res_it.data() != next_word_res; word_res_it.forward()) { if (!word_res_it.data()->part_of_combo) { if (prev_row_res == row_res) prev_word_res = word_res; word_res = word_res_it.data(); } } ASSERT_HOST(!word_res_it.cycled_list()); word_res_it.forward(); } else { // word_res_it is OK, but reset word_res and prev_word_res if needed. WERD_RES_IT wr_it(&row_res->word_res_list); for (wr_it.mark_cycle_pt(); !wr_it.cycled_list(); wr_it.forward()) { if (!wr_it.data()->part_of_combo) { if (prev_row_res == row_res) prev_word_res = word_res; word_res = wr_it.data(); } } } } /************************************************************************* * PAGE_RES_IT::internal_forward * * Find the next word on the page. If empty_ok is true, then non-text blocks * and text blocks with no text are visited as if they contain a single * imaginary word in a single imaginary row. (word() and row() both return NULL * in such a block and the return value is NULL.) * If empty_ok is false, the old behaviour is maintained. Each real word * is visited and empty and non-text blocks and rows are skipped. * new_block is used to initialize the iterators for a new block. * The iterator maintains pointers to block, row and word for the previous, * current and next words. These are correct, regardless of block/row * boundaries. NULL values denote start and end of the page. *************************************************************************/ WERD_RES *PAGE_RES_IT::internal_forward(bool new_block, bool empty_ok) { bool new_row = false; prev_block_res = block_res; prev_row_res = row_res; prev_word_res = word_res; block_res = next_block_res; row_res = next_row_res; word_res = next_word_res; next_block_res = NULL; next_row_res = NULL; next_word_res = NULL; while (!block_res_it.cycled_list()) { if (new_block) { new_block = false; row_res_it.set_to_list(&block_res_it.data()->row_res_list); row_res_it.mark_cycle_pt(); if (row_res_it.empty() && empty_ok) { next_block_res = block_res_it.data(); break; } new_row = true; } while (!row_res_it.cycled_list()) { if (new_row) { new_row = false; word_res_it.set_to_list(&row_res_it.data()->word_res_list); word_res_it.mark_cycle_pt(); } // Skip any part_of_combo words. while (!word_res_it.cycled_list() && word_res_it.data()->part_of_combo) word_res_it.forward(); if (!word_res_it.cycled_list()) { next_block_res = block_res_it.data(); next_row_res = row_res_it.data(); next_word_res = word_res_it.data(); word_res_it.forward(); goto foundword; } // end of row reached row_res_it.forward(); new_row = true; } // end of block reached block_res_it.forward(); new_block = true; } foundword: // Update prev_word_best_choice pointer. if (page_res != NULL && page_res->prev_word_best_choice != NULL) { *page_res->prev_word_best_choice = (new_block || prev_word_res == NULL) ? NULL : prev_word_res->best_choice; } return word_res; } /************************************************************************* * PAGE_RES_IT::restart_row() * * Move to the beginning (leftmost word) of the current row. *************************************************************************/ WERD_RES *PAGE_RES_IT::restart_row() { ROW_RES *row = this->row(); if (!row) return NULL; for (restart_page(); this->row() != row; forward()) { // pass } return word(); } /************************************************************************* * PAGE_RES_IT::forward_paragraph * * Move to the beginning of the next paragraph, allowing empty blocks. *************************************************************************/ WERD_RES *PAGE_RES_IT::forward_paragraph() { while (block_res == next_block_res && (next_row_res != NULL && next_row_res->row != NULL && row_res->row->para() == next_row_res->row->para())) { internal_forward(false, true); } return internal_forward(false, true); } /************************************************************************* * PAGE_RES_IT::forward_block * * Move to the beginning of the next block, allowing empty blocks. *************************************************************************/ WERD_RES *PAGE_RES_IT::forward_block() { while (block_res == next_block_res) { internal_forward(false, true); } return internal_forward(false, true); } void PAGE_RES_IT::rej_stat_word() { inT16 chars_in_word; inT16 rejects_in_word = 0; chars_in_word = word_res->reject_map.length (); page_res->char_count += chars_in_word; block_res->char_count += chars_in_word; row_res->char_count += chars_in_word; rejects_in_word = word_res->reject_map.reject_count (); page_res->rej_count += rejects_in_word; block_res->rej_count += rejects_in_word; row_res->rej_count += rejects_in_word; if (chars_in_word == rejects_in_word) row_res->whole_word_rej_count += rejects_in_word; } tesseract-3.04.01/ccstruct/pageres.h000066400000000000000000000765501266071204500173250ustar00rootroot00000000000000/********************************************************************** * File: pageres.h (Formerly page_res.h) * Description: Results classes used by control.c * Author: Phil Cheatle * Created: Tue Sep 22 08:42:49 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 PAGERES_H #define PAGERES_H #include "blamer.h" #include "blobs.h" #include "boxword.h" #include "elst.h" #include "genericvector.h" #include "normalis.h" #include "ocrblock.h" #include "ocrrow.h" #include "params_training_featdef.h" #include "ratngs.h" #include "rejctmap.h" #include "seam.h" #include "werd.h" namespace tesseract { struct FontInfo; class Tesseract; } using tesseract::FontInfo; /* Forward declarations */ class BLOCK_RES; ELISTIZEH (BLOCK_RES) CLISTIZEH (BLOCK_RES) class ROW_RES; ELISTIZEH (ROW_RES) class WERD_RES; ELISTIZEH (WERD_RES) /************************************************************************* * PAGE_RES - Page results *************************************************************************/ class PAGE_RES { // page result public: inT32 char_count; inT32 rej_count; BLOCK_RES_LIST block_res_list; BOOL8 rejected; // Updated every time PAGE_RES_IT iterating on this PAGE_RES moves to // the next word. This pointer is not owned by PAGE_RES class. WERD_CHOICE **prev_word_best_choice; // Sums of blame reasons computed by the blamer. GenericVector blame_reasons; // Debug information about all the misadaptions on this page. // Each BlamerBundle contains an index into this vector, so that words that // caused misadaption could be marked. However, since words could be // deleted/split/merged, the log is stored on the PAGE_RES level. GenericVector misadaption_log; inline void Init() { char_count = 0; rej_count = 0; rejected = FALSE; prev_word_best_choice = NULL; blame_reasons.init_to_size(IRR_NUM_REASONS, 0); } PAGE_RES() { Init(); } // empty constructor PAGE_RES(bool merge_similar_words, BLOCK_LIST *block_list, // real blocks WERD_CHOICE **prev_word_best_choice_ptr); ~PAGE_RES () { // destructor } }; /************************************************************************* * BLOCK_RES - Block results *************************************************************************/ class BLOCK_RES:public ELIST_LINK { public: BLOCK * block; // real block inT32 char_count; // chars in block inT32 rej_count; // rejected chars inT16 font_class; // inT16 row_count; float x_height; BOOL8 font_assigned; // block already // processed BOOL8 bold; // all bold BOOL8 italic; // all italic ROW_RES_LIST row_res_list; BLOCK_RES() { } // empty constructor BLOCK_RES(bool merge_similar_words, BLOCK *the_block); // real block ~BLOCK_RES () { // destructor } }; /************************************************************************* * ROW_RES - Row results *************************************************************************/ class ROW_RES:public ELIST_LINK { public: ROW * row; // real row inT32 char_count; // chars in block inT32 rej_count; // rejected chars inT32 whole_word_rej_count; // rejs in total rej wds WERD_RES_LIST word_res_list; ROW_RES() { } // empty constructor ROW_RES(bool merge_similar_words, ROW *the_row); // real row ~ROW_RES() { // destructor } }; /************************************************************************* * WERD_RES - Word results *************************************************************************/ enum CRUNCH_MODE { CR_NONE, CR_KEEP_SPACE, CR_LOOSE_SPACE, CR_DELETE }; // WERD_RES is a collection of publicly accessible members that gathers // information about a word result. class WERD_RES : public ELIST_LINK { public: // Which word is which? // There are 3 coordinate spaces in use here: a possibly rotated pixel space, // the original image coordinate space, and the BLN space in which the // baseline of a word is at kBlnBaselineOffset, the xheight is kBlnXHeight, // and the x-middle of the word is at 0. // In the rotated pixel space, coordinates correspond to the input image, // but may be rotated about the origin by a multiple of 90 degrees, // and may therefore be negative. // In any case a rotation by denorm.block()->re_rotation() will take them // back to the original image. // The other differences between words all represent different stages of // processing during recognition. // ---------------------------INPUT------------------------------------- // The word is the input C_BLOBs in the rotated pixel space. // word is NOT owned by the WERD_RES unless combination is true. // All the other word pointers ARE owned by the WERD_RES. WERD* word; // Input C_BLOB word. // -------------SETUP BY SetupFor*Recognition---READONLY-INPUT------------ // The bln_boxes contains the bounding boxes (only) of the input word, in the // BLN space. The lengths of word and bln_boxes // match as they are both before any chopping. // 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, 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. // ----Initialized by SetupFor*Recognition---BUT OUTPUT FROM RECOGNITION---- // ----Setup to a (different!) state expected by the various classifiers---- // TODO(rays) Tidy and make more consistent. // The chopped_word is also in BLN space, and represents the fully chopped // 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. // 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; // --------------OUTPUT FROM RECOGNITION------------------------------- // --------------Not all fields are necessarily set.------------------- // ---best_choice, raw_choice *must* end up set, with a box_word------- // ---In complete output, the number of blobs in rebuild_word matches--- // ---the number of boxes in box_word, the number of unichar_ids in--- // ---best_choice, the number of ints in best_state, and the number--- // ---of strings in correct_text-------------------------------------- // ---SetupFake Sets everything to appropriate values if the word is--- // ---known to be bad before recognition.------------------------------ // The rebuild_word is also in BLN space, but represents the final best // segmentation of the word. Its length is therefore the same as box_word. TWERD* rebuild_word; // BLN best segmented word. // The box_word is in the original image coordinate space. It is the // bounding boxes of the rebuild_word, after denormalization. // The length of box_word matches rebuild_word, best_state (if set) and // correct_text (if set), as well as best_choice and represents the // number of classified units in the output. tesseract::BoxWord* box_word; // Denormalized output boxes. // The best_state stores the relationship between chopped_word and // rebuild_word. Each blob[i] in rebuild_word is composed of best_state[i] // adjacent blobs in chopped_word. The seams in seam_array are hidden // within a rebuild_word blob and revealed between them. GenericVector best_state; // Number of blobs in each best blob. // The correct_text is used during training and adaption to carry the // text to the training system without the need for a unicharset. There // is one entry in the vector for each blob in rebuild_word and box_word. GenericVector correct_text; // The Tesseract that was used to recognize this word. Just a borrowed // pointer. Note: Tesseract's class definition is in a higher-level library. // We avoid introducing a cyclic dependency by not using the Tesseract // within WERD_RES. We are just storing it to provide access to it // for the top-level multi-language controller, and maybe for output of // the recognized language. tesseract::Tesseract* tesseract; // Less-well documented members. // TODO(rays) Add more documentation here. WERD_CHOICE *ep_choice; // ep text TODO(rays) delete this. REJMAP reject_map; // best_choice rejects BOOL8 tess_failed; /* If tess_failed is TRUE, one of the following tests failed when Tess returned: - The outword blob list was not the same length as the best_choice string; - The best_choice string contained ALL blanks; - The best_choice string was zero length */ BOOL8 tess_accepted; // Tess thinks its ok? BOOL8 tess_would_adapt; // Tess would adapt? BOOL8 done; // ready for output? bool small_caps; // word appears to be small caps bool odd_size; // word is bigger than line or leader dots. inT8 italic; inT8 bold; // The fontinfos are pointers to data owned by the classifier. const FontInfo* fontinfo; const FontInfo* fontinfo2; inT8 fontinfo_id_count; // number of votes inT8 fontinfo_id2_count; // number of votes BOOL8 guessed_x_ht; BOOL8 guessed_caps_ht; CRUNCH_MODE unlv_crunch_mode; float x_height; // post match estimate float caps_height; // post match estimate float baseline_shift; // post match estimate. /* To deal with fuzzy spaces we need to be able to combine "words" to form combinations when we suspect that the gap is a non-space. The (new) text ord code generates separate words for EVERY fuzzy gap - flags in the word indicate whether the gap is below the threshold (fuzzy kern) and is thus NOT a real word break by default, or above the threshold (fuzzy space) and this is a real word break by default. The WERD_RES list contains all these words PLUS "combination" words built out of (copies of) the words split by fuzzy kerns. The separate parts have their "part_of_combo" flag set true and should be IGNORED on a default reading of the list. Combination words are FOLLOWED by the sequence of part_of_combo words which they combine. */ BOOL8 combination; //of two fuzzy gap wds BOOL8 part_of_combo; //part of a combo BOOL8 reject_spaces; //Reject spacing? WERD_RES() { InitNonPointers(); InitPointers(); } WERD_RES(WERD *the_word) { InitNonPointers(); InitPointers(); word = the_word; } // Deep copies everything except the ratings MATRIX. // To get that use deep_copy below. WERD_RES(const WERD_RES &source) : ELIST_LINK(source) { InitPointers(); *this = source; // see operator= } ~WERD_RES(); // Returns the UTF-8 string for the given blob index in the best_choice word, // given that we know whether we are in a right-to-left reading context. // This matters for mirrorable characters such as parentheses. We recognize // characters purely based on their shape on the page, and by default produce // the corresponding unicode for a left-to-right context. const char* BestUTF8(int blob_index, bool in_rtl_context) const { if (blob_index < 0 || best_choice == NULL || blob_index >= best_choice->length()) return NULL; UNICHAR_ID id = best_choice->unichar_id(blob_index); if (id < 0 || id >= uch_set->size() || id == INVALID_UNICHAR_ID) return NULL; UNICHAR_ID mirrored = uch_set->get_mirror(id); if (in_rtl_context && mirrored > 0 && mirrored != INVALID_UNICHAR_ID) id = mirrored; return uch_set->id_to_unichar_ext(id); } // Returns the UTF-8 string for the given blob index in the raw_choice word. const char* RawUTF8(int blob_index) const { if (blob_index < 0 || blob_index >= raw_choice->length()) return NULL; UNICHAR_ID id = raw_choice->unichar_id(blob_index); if (id < 0 || id >= uch_set->size() || id == INVALID_UNICHAR_ID) return NULL; return uch_set->id_to_unichar(id); } UNICHARSET::Direction SymbolDirection(int blob_index) const { if (best_choice == NULL || blob_index >= best_choice->length() || blob_index < 0) return UNICHARSET::U_OTHER_NEUTRAL; return uch_set->get_direction(best_choice->unichar_id(blob_index)); } bool AnyRtlCharsInWord() const { if (uch_set == NULL || best_choice == NULL || best_choice->length() < 1) return false; for (int id = 0; id < best_choice->length(); id++) { int unichar_id = best_choice->unichar_id(id); if (unichar_id < 0 || unichar_id >= uch_set->size()) continue; // Ignore illegal chars. UNICHARSET::Direction dir = uch_set->get_direction(unichar_id); if (dir == UNICHARSET::U_RIGHT_TO_LEFT || dir == UNICHARSET::U_RIGHT_TO_LEFT_ARABIC || dir == UNICHARSET::U_ARABIC_NUMBER) return true; } return false; } bool AnyLtrCharsInWord() const { if (uch_set == NULL || best_choice == NULL || best_choice->length() < 1) return false; for (int id = 0; id < best_choice->length(); id++) { int unichar_id = best_choice->unichar_id(id); if (unichar_id < 0 || unichar_id >= uch_set->size()) continue; // Ignore illegal chars. UNICHARSET::Direction dir = uch_set->get_direction(unichar_id); if (dir == UNICHARSET::U_LEFT_TO_RIGHT) return true; } return false; } // Return whether the blobs in this WERD_RES 0, 1,... come from an engine // that gave us the unichars in reading order (as opposed to strict left // to right). bool UnicharsInReadingOrder() const { return best_choice->unichars_in_script_order(); } void InitNonPointers(); 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); // Initializes a blank (default constructed) WERD_RES from one that has // already been recognized. // Use SetupFor*Recognition afterwards to complete the setup and make // it ready for a retry recognition. void InitForRetryRecognition(const WERD_RES& source); // 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. // The norm_mode sets the default mode for normalization in absence // of any of the above flags. It should really be a tesseract::OcrEngineMode // but is declared as int for ease of use with tessedit_ocr_engine_mode. // Returns false if the word is empty and sets up fake results. bool SetupForRecognition(const UNICHARSET& unicharset_in, tesseract::Tesseract* tesseract, Pix* pix, int norm_mode, const TBOX* norm_box, bool numeric_mode, bool use_body_size, bool allow_detailed_fx, ROW *row, const 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 for an empty recognition result: // bln_boxes, chopped_word, seam_array, denorm, best_choice, raw_choice. void SetupFake(const UNICHARSET& uch); // Set the word as having the script of the input unicharset. void SetupWordScript(const UNICHARSET& unicharset_in); // 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); // Prints the top choice along with the accepted/done flags. void DebugTopChoice(const char* msg) const; // 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); // delete word; // is simpler and faster than: // word1 = *word; // delete word; // as it doesn't need to copy and reallocate anything. void ConsumeWordResults(WERD_RES* word); // Replace the best choice and rebuild box word. // choice must be from the current best_choices list. void ReplaceBestChoice(WERD_CHOICE* choice); // 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. // Also sets up the output box_word. void CloneChoppedToRebuild(); // Sets/replaces the box_word with one made from the rebuild_word. void SetupBoxWord(); // 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(); // 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 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(); // Merges 2 adjacent blobs in the result if the permanent callback // class_cb returns other than INVALID_UNICHAR_ID, AND the permanent // callback box_cb is NULL or returns true, setting the merged blob // result to the class returned from class_cb. // Returns true if anything was merged. bool ConditionalBlobMerge( TessResultCallback2* class_cb, 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(); // Callback helper for fix_hyphens returns UNICHAR_ID of - if both // arguments are hyphen, otherwise INVALID_UNICHAR_ID. UNICHAR_ID BothHyphens(UNICHAR_ID id1, UNICHAR_ID id2); // 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(); // 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) { 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). // Since this may be called bidirectionally OR both the BOL and EOL flags. void copy_on(WERD_RES *word_res) { //from this word word->set_flag(W_BOL, word->flag(W_BOL) || word_res->word->flag(W_BOL)); word->set_flag(W_EOL, word->flag(W_EOL) || word_res->word->flag(W_EOL)); word->copy_on(word_res->word); } // Returns true if the collection of count pieces, starting at start, are all // natural connected components, ie there are no real chops involved. bool PiecesAllNatural(int start, int count) const; }; /************************************************************************* * PAGE_RES_IT - Page results iterator *************************************************************************/ class PAGE_RES_IT { public: PAGE_RES * page_res; // page being iterated PAGE_RES_IT() { } // empty contructor PAGE_RES_IT(PAGE_RES *the_page_res) { // page result page_res = the_page_res; restart_page(); // ready to scan } // Do two PAGE_RES_ITs point at the same word? // This is much cheaper than cmp(). bool operator ==(const PAGE_RES_IT &other) const; bool operator !=(const PAGE_RES_IT &other) const {return !(*this == other); } // Given another PAGE_RES_IT to the same page, // this before other: -1 // this equal to other: 0 // this later than other: 1 int cmp(const PAGE_RES_IT &other) const; WERD_RES *restart_page() { return start_page(false); // Skip empty blocks. } WERD_RES *restart_page_with_empties() { return start_page(true); // Allow empty blocks. } WERD_RES *start_page(bool empty_ok); WERD_RES *restart_row(); // ============ Methods that mutate the underling structures =========== // Note that these methods will potentially invalidate other PAGE_RES_ITs // and are intended to be used only while a single PAGE_RES_IT is active. // This problem needs to be taken into account if these mutation operators // are ever provided to PageIterator or its subclasses. // Inserts the new_word and a corresponding WERD_RES before the current // position. The simple fields of the WERD_RES are copied from clone_res and // the resulting WERD_RES is returned for further setup with best_choice etc. WERD_RES* InsertSimpleCloneWord(const WERD_RES& clone_res, WERD* new_word); // Replaces the current WERD/WERD_RES with the given words. The given words // contain fake blobs that indicate the position of the characters. These are // replaced with real blobs from the current word as much as possible. void ReplaceCurrentWord(tesseract::PointerVector* words); // Deletes the current WERD_RES and its underlying WERD. void DeleteCurrentWord(); // Makes the current word a fuzzy space if not already fuzzy. Updates // corresponding part of combo if required. void MakeCurrentWordFuzzy(); WERD_RES *forward() { // Get next word. return internal_forward(false, false); } // Move forward, but allow empty blocks to show as single NULL words. WERD_RES *forward_with_empties() { return internal_forward(false, true); } WERD_RES *forward_paragraph(); // get first word in next non-empty paragraph WERD_RES *forward_block(); // get first word in next non-empty block WERD_RES *prev_word() const { // previous word return prev_word_res; } ROW_RES *prev_row() const { // row of prev word return prev_row_res; } BLOCK_RES *prev_block() const { // block of prev word return prev_block_res; } WERD_RES *word() const { // current word return word_res; } ROW_RES *row() const { // row of current word return row_res; } BLOCK_RES *block() const { // block of cur. word return block_res; } WERD_RES *next_word() const { // next word return next_word_res; } ROW_RES *next_row() const { // row of next word return next_row_res; } BLOCK_RES *next_block() const { // block of next word return next_block_res; } void rej_stat_word(); // for page/block/row void ResetWordIterator(); private: WERD_RES *internal_forward(bool new_block, bool empty_ok); WERD_RES * prev_word_res; // previous word ROW_RES *prev_row_res; // row of prev word BLOCK_RES *prev_block_res; // block of prev word WERD_RES *word_res; // current word ROW_RES *row_res; // row of current word BLOCK_RES *block_res; // block of cur. word WERD_RES *next_word_res; // next word ROW_RES *next_row_res; // row of next word BLOCK_RES *next_block_res; // block of next word BLOCK_RES_IT block_res_it; // iterators ROW_RES_IT row_res_it; WERD_RES_IT word_res_it; }; #endif tesseract-3.04.01/ccstruct/params_training_featdef.cpp000066400000000000000000000026131266071204500230530ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // 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 tesseract-3.04.01/ccstruct/params_training_featdef.h000066400000000000000000000134421266071204500225220ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: params_training_featdef.h // Description: Feature definitions for params training. // Author: Rika Antonova // Created: Mon Nov 28 11:26:42 PDT 2011 // // (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_TRAINING_FEATDEF_H_ #define TESSERACT_WORDREC_PARAMS_TRAINING_FEATDEF_H_ #include "genericvector.h" #include "strngs.h" 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 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 // 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_FEATURE_TYPES }; 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() : 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_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. typedef GenericVector ParamsTrainingHypothesisList; // A bundle that accumulates all of the hypothesis lists explored during all // of the runs of segmentation search on a word (e.g. a list of hypotheses // explored on PASS1, PASS2, fix xheight pass, etc). class ParamsTrainingBundle { public: ParamsTrainingBundle() {}; // Starts a new hypothesis list. // Should be called at the beginning of a new run of the segmentation search. void StartHypothesisList() { hyp_list_vec.push_back(ParamsTrainingHypothesisList()); } // Adds a new ParamsTrainingHypothesis to the current hypothesis list // and returns the reference to the newly added entry. ParamsTrainingHypothesis &AddHypothesis( const ParamsTrainingHypothesis &other) { if (hyp_list_vec.empty()) StartHypothesisList(); hyp_list_vec.back().push_back(ParamsTrainingHypothesis(other)); return hyp_list_vec.back().back(); } GenericVector hyp_list_vec; }; } // namespace tesseract #endif // TESSERACT_WORDREC_PARAMS_TRAINING_FEATDEF_H_ tesseract-3.04.01/ccstruct/pdblock.cpp000066400000000000000000000325171266071204500176430ustar00rootroot00000000000000/********************************************************************** * File: pdblock.c (Formerly pdblk.c) * Description: PDBLK member functions and iterator functions. * Author: Ray Smith * Created: Fri Mar 15 09:41:28 GMT 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include #include "allheaders.h" #include "blckerr.h" #include "pdblock.h" // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #define BLOCK_LABEL_HEIGHT 150 //char height of block id CLISTIZE (PDBLK) /********************************************************************** * PDBLK::PDBLK * * Constructor for a simple rectangular block. **********************************************************************/ PDBLK::PDBLK ( //rectangular block inT16 xmin, //bottom left inT16 ymin, inT16 xmax, //top right inT16 ymax): box (ICOORD (xmin, ymin), ICOORD (xmax, ymax)) { //boundaries ICOORDELT_IT left_it = &leftside; ICOORDELT_IT right_it = &rightside; hand_poly = NULL; left_it.set_to_list (&leftside); right_it.set_to_list (&rightside); //make default box left_it.add_to_end (new ICOORDELT (xmin, ymin)); left_it.add_to_end (new ICOORDELT (xmin, ymax)); right_it.add_to_end (new ICOORDELT (xmax, ymin)); right_it.add_to_end (new ICOORDELT (xmax, ymax)); index_ = 0; } /********************************************************************** * PDBLK::set_sides * * Sets left and right vertex lists **********************************************************************/ void PDBLK::set_sides( //set vertex lists ICOORDELT_LIST *left, //left vertices ICOORDELT_LIST *right //right vertices ) { //boundaries ICOORDELT_IT left_it = &leftside; ICOORDELT_IT right_it = &rightside; leftside.clear (); left_it.move_to_first (); left_it.add_list_before (left); rightside.clear (); right_it.move_to_first (); right_it.add_list_before (right); } /********************************************************************** * PDBLK::contains * * Return TRUE if the given point is within the block. **********************************************************************/ BOOL8 PDBLK::contains( //test containment ICOORD pt //point to test ) { BLOCK_RECT_IT it = this; //rectangle iterator ICOORD bleft, tright; //corners of rectangle for (it.start_block (); !it.cycled_rects (); it.forward ()) { //get rectangle it.bounding_box (bleft, tright); //inside rect if (pt.x () >= bleft.x () && pt.x () <= tright.x () && pt.y () >= bleft.y () && pt.y () <= tright.y ()) return TRUE; //is inside } return FALSE; //not inside } /********************************************************************** * PDBLK::move * * Reposition block **********************************************************************/ void PDBLK::move( // reposition block const ICOORD vec // by vector ) { ICOORDELT_IT it(&leftside); for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) *(it.data ()) += vec; it.set_to_list (&rightside); for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) *(it.data ()) += vec; box.move (vec); } // Returns a binary Pix mask with a 1 pixel for every pixel within the // block. Rotates the coordinate system by rerotation prior to rendering. Pix* PDBLK::render_mask(const FCOORD& rerotation, TBOX* mask_box) { TBOX rotated_box(box); rotated_box.rotate(rerotation); Pix* pix = pixCreate(rotated_box.width(), rotated_box.height(), 1); if (hand_poly != NULL) { // We are going to rotate, so get a deep copy of the points and // make a new POLY_BLOCK with it. ICOORDELT_LIST polygon; polygon.deep_copy(hand_poly->points(), ICOORDELT::deep_copy); POLY_BLOCK image_block(&polygon, hand_poly->isA()); image_block.rotate(rerotation); // Block outline is a polygon, so use a PB_LINE_IT to get the // rasterized interior. (Runs of interior pixels on a line.) PB_LINE_IT *lines = new PB_LINE_IT(&image_block); for (int y = box.bottom(); y < box.top(); ++y) { ICOORDELT_LIST* segments = lines->get_line(y); if (!segments->empty()) { ICOORDELT_IT s_it(segments); // Each element of segments is a start x and x size of the // run of interior pixels. for (s_it.mark_cycle_pt(); !s_it.cycled_list(); s_it.forward()) { int start = s_it.data()->x(); int xext = s_it.data()->y(); // Set the run of pixels to 1. pixRasterop(pix, start - rotated_box.left(), rotated_box.height() - 1 - (y - rotated_box.bottom()), xext, 1, PIX_SET, NULL, 0, 0); } } delete segments; } delete lines; } else { // Just fill the whole block as there is only a bounding box. pixRasterop(pix, 0, 0, rotated_box.width(), rotated_box.height(), PIX_SET, NULL, 0, 0); } if (mask_box != NULL) *mask_box = rotated_box; return pix; } /********************************************************************** * PDBLK::plot * * Plot the outline of a block in the given colour. **********************************************************************/ #ifndef GRAPHICS_DISABLED void PDBLK::plot( //draw outline ScrollView* window, //window to draw in inT32 serial, //serial number ScrollView::Color colour //colour to draw in ) { ICOORD startpt; //start of outline ICOORD endpt; //end of outline ICOORD prevpt; //previous point ICOORDELT_IT it = &leftside; //iterator //set the colour window->Pen(colour); window->TextAttributes("Times", BLOCK_LABEL_HEIGHT, false, false, false); if (hand_poly != NULL) { hand_poly->plot(window, serial); } else if (!leftside.empty ()) { startpt = *(it.data ()); //bottom left corner // tprintf("Block %d bottom left is (%d,%d)\n", // serial,startpt.x(),startpt.y()); char temp_buff[34]; #if defined(__UNIX__) || defined(MINGW) sprintf(temp_buff, INT32FORMAT, serial); #else ultoa (serial, temp_buff, 10); #endif window->Text(startpt.x (), startpt.y (), temp_buff); window->SetCursor(startpt.x (), startpt.y ()); do { prevpt = *(it.data ()); //previous point it.forward (); //move to next point //draw round corner window->DrawTo(prevpt.x (), it.data ()->y ()); window->DrawTo(it.data ()->x (), it.data ()->y ()); } while (!it.at_last ()); //until end of list endpt = *(it.data ()); //end point //other side of boundary window->SetCursor(startpt.x (), startpt.y ()); it.set_to_list (&rightside); prevpt = startpt; for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { //draw round corner window->DrawTo(prevpt.x (), it.data ()->y ()); window->DrawTo(it.data ()->x (), it.data ()->y ()); prevpt = *(it.data ()); //previous point } //close boundary window->DrawTo(endpt.x(), endpt.y()); } } #endif /********************************************************************** * PDBLK::operator= * * Assignment - duplicate the block structure, but with an EMPTY row list. **********************************************************************/ PDBLK & PDBLK::operator= ( //assignment const PDBLK & source //from this ) { // this->ELIST_LINK::operator=(source); if (!leftside.empty ()) leftside.clear (); if (!rightside.empty ()) rightside.clear (); leftside.deep_copy(&source.leftside, &ICOORDELT::deep_copy); rightside.deep_copy(&source.rightside, &ICOORDELT::deep_copy); box = source.box; return *this; } /********************************************************************** * BLOCK_RECT_IT::BLOCK_RECT_IT * * Construct a block rectangle iterator. **********************************************************************/ BLOCK_RECT_IT::BLOCK_RECT_IT ( //iterate rectangles PDBLK * blkptr //from block ):left_it (&blkptr->leftside), right_it (&blkptr->rightside) { block = blkptr; //remember block //non empty list if (!blkptr->leftside.empty ()) { start_block(); //ready for iteration } } /********************************************************************** * BLOCK_RECT_IT::set_to_block * * Start a new block. **********************************************************************/ void BLOCK_RECT_IT::set_to_block( //start (new) block PDBLK *blkptr) { //block to start block = blkptr; //remember block //set iterators left_it.set_to_list (&blkptr->leftside); right_it.set_to_list (&blkptr->rightside); if (!blkptr->leftside.empty ()) start_block(); //ready for iteration } /********************************************************************** * BLOCK_RECT_IT::start_block * * Restart a block. **********************************************************************/ void BLOCK_RECT_IT::start_block() { //start (new) block left_it.move_to_first (); right_it.move_to_first (); left_it.mark_cycle_pt (); right_it.mark_cycle_pt (); ymin = left_it.data ()->y (); //bottom of first box ymax = left_it.data_relative (1)->y (); if (right_it.data_relative (1)->y () < ymax) //smallest step ymax = right_it.data_relative (1)->y (); } /********************************************************************** * BLOCK_RECT_IT::forward * * Move to the next rectangle in the block. **********************************************************************/ void BLOCK_RECT_IT::forward() { //next rectangle if (!left_it.empty ()) { //non-empty list if (left_it.data_relative (1)->y () == ymax) left_it.forward (); //move to meet top if (right_it.data_relative (1)->y () == ymax) right_it.forward (); //last is special if (left_it.at_last () || right_it.at_last ()) { left_it.move_to_first (); //restart right_it.move_to_first (); //now at bottom ymin = left_it.data ()->y (); } else { ymin = ymax; //new bottom } //next point ymax = left_it.data_relative (1)->y (); if (right_it.data_relative (1)->y () < ymax) //least step forward ymax = right_it.data_relative (1)->y (); } } /********************************************************************** * BLOCK_LINE_IT::get_line * * Get the the start and width of a line in the block. **********************************************************************/ inT16 BLOCK_LINE_IT::get_line( //get a line inT16 y, //line to get inT16 &xext //output extent ) { ICOORD bleft; //bounding box ICOORD tright; //of block & rect //get block box block->bounding_box (bleft, tright); if (y < bleft.y () || y >= tright.y ()) { // block->print(stderr,FALSE); BADBLOCKLINE.error ("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y); } //get rectangle box rect_it.bounding_box (bleft, tright); //inside rectangle if (y >= bleft.y () && y < tright.y ()) { //width of line xext = tright.x () - bleft.x (); return bleft.x (); //start of line } for (rect_it.start_block (); !rect_it.cycled_rects (); rect_it.forward ()) { //get rectangle box rect_it.bounding_box (bleft, tright); //inside rectangle if (y >= bleft.y () && y < tright.y ()) { //width of line xext = tright.x () - bleft.x (); return bleft.x (); //start of line } } LOSTBLOCKLINE.error ("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y); return 0; //dummy to stop warning } tesseract-3.04.01/ccstruct/pdblock.h000066400000000000000000000124101266071204500172760ustar00rootroot00000000000000/********************************************************************** * File: pdblock.h (Formerly pdblk.h) * Description: Page block class definition. * Author: Ray Smith * Created: Thu Mar 14 17:32:01 GMT 1991 * * (C) Copyright 1991, 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 PDBLOCK_H #define PDBLOCK_H #include "clst.h" #include "strngs.h" #include "polyblk.h" class DLLSYM PDBLK; //forward decl struct Pix; CLISTIZEH (PDBLK) ///page block class PDBLK { friend class BLOCK_RECT_IT; //< block iterator public: ///empty constructor PDBLK() { hand_poly = NULL; index_ = 0; } ///simple constructor PDBLK(inT16 xmin, //< bottom left inT16 ymin, inT16 xmax, //< top right inT16 ymax); ///set vertex lists ///@param left list of left vertices ///@param right list of right vertices void set_sides(ICOORDELT_LIST *left, ICOORDELT_LIST *right); ///destructor ~PDBLK () { if (hand_poly) delete hand_poly; } POLY_BLOCK *poly_block() const { return hand_poly; } ///set the poly block void set_poly_block(POLY_BLOCK *blk) { hand_poly = blk; } ///get box void bounding_box(ICOORD &bottom_left, //bottom left ICOORD &top_right) const { //topright bottom_left = box.botleft (); top_right = box.topright (); } ///get real box const TBOX &bounding_box() const { return box; } int index() const { return index_; } void set_index(int value) { index_ = value; } ///is pt inside block BOOL8 contains(ICOORD pt); /// reposition block void move(const ICOORD vec); // by vector // Returns a binary Pix mask with a 1 pixel for every pixel within the // block. Rotates the coordinate system by rerotation prior to rendering. // If not NULL, mask_box is filled with the position box of the returned // mask image. Pix *render_mask(const FCOORD &rerotation, TBOX *mask_box); #ifndef GRAPHICS_DISABLED ///draw histogram ///@param window window to draw in ///@param serial serial number ///@param colour colour to draw in void plot(ScrollView* window, inT32 serial, ScrollView::Color colour); #endif // GRAPHICS_DISABLED ///assignment ///@param source from this PDBLK & operator= (const PDBLK & source); protected: POLY_BLOCK *hand_poly; //< weird as well ICOORDELT_LIST leftside; //< left side vertices ICOORDELT_LIST rightside; //< right side vertices TBOX box; //< bounding box int index_; //< Serial number of this block. }; class DLLSYM BLOCK_RECT_IT //rectangle iterator { public: ///constructor ///@param blkptr block to iterate BLOCK_RECT_IT(PDBLK *blkptr); ///start (new) block void set_to_block ( PDBLK * blkptr); //block to iterate ///start iteration void start_block(); ///next rectangle void forward(); ///test end BOOL8 cycled_rects() { return left_it.cycled_list () && right_it.cycled_list (); } ///current rectangle ///@param bleft bottom left ///@param tright top right void bounding_box(ICOORD &bleft, ICOORD &tright) { //bottom left bleft = ICOORD (left_it.data ()->x (), ymin); //top right tright = ICOORD (right_it.data ()->x (), ymax); } private: inT16 ymin; //< bottom of rectangle inT16 ymax; //< top of rectangle PDBLK *block; //< block to iterate ICOORDELT_IT left_it; //< boundary iterators ICOORDELT_IT right_it; }; ///rectangle iterator class DLLSYM BLOCK_LINE_IT { public: ///constructor ///@param blkptr from block BLOCK_LINE_IT (PDBLK * blkptr) :rect_it (blkptr) { block = blkptr; //remember block } ///start (new) block ///@param blkptr block to start void set_to_block (PDBLK * blkptr) { block = blkptr; //remember block //set iterator rect_it.set_to_block (blkptr); } ///get a line ///@param y line to get ///@param xext output extent inT16 get_line(inT16 y, inT16 &xext); private: PDBLK * block; //< block to iterate BLOCK_RECT_IT rect_it; //< rectangle iterator }; int decreasing_top_order(const void *row1, const void *row2); #endif tesseract-3.04.01/ccstruct/points.cpp000066400000000000000000000120711266071204500175320ustar00rootroot00000000000000/********************************************************************** * File: points.c (Formerly coords.c) * Description: Member functions for coordinate classes. * Author: Ray Smith * Created: Fri Mar 15 08:58:17 GMT 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #ifdef _MSC_VER #define _USE_MATH_DEFINES #endif // _MSC_VER #include #include "helpers.h" #include "ndminx.h" #include "serialis.h" #include "points.h" ELISTIZE (ICOORDELT) //turn to list bool FCOORD::normalise() { //Convert to unit vec float len = length (); if (len < 0.0000000001) { return false; } xcoord /= len; ycoord /= len; return true; } // Set from the given x,y, shrinking the vector to fit if needed. void ICOORD::set_with_shrink(int x, int y) { // Fit the vector into an ICOORD, which is 16 bit. int factor = 1; int max_extent = MAX(abs(x), abs(y)); if (max_extent > MAX_INT16) factor = max_extent / MAX_INT16 + 1; xcoord = x / factor; ycoord = y / factor; } // The fortran/basic sgn function returns -1, 0, 1 if x < 0, x == 0, x > 0 // respectively. static int sign(int x) { if (x < 0) return -1; else return x > 0 ? 1 : 0; } // Writes to the given file. Returns false in case of error. bool ICOORD::Serialize(FILE* fp) const { if (fwrite(&xcoord, sizeof(xcoord), 1, fp) != 1) return false; if (fwrite(&ycoord, sizeof(ycoord), 1, fp) != 1) return false; return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool ICOORD::DeSerialize(bool swap, FILE* fp) { if (fread(&xcoord, sizeof(xcoord), 1, fp) != 1) return false; if (fread(&ycoord, sizeof(ycoord), 1, fp) != 1) return false; if (swap) { ReverseN(&xcoord, sizeof(xcoord)); ReverseN(&ycoord, sizeof(ycoord)); } return true; } // Setup for iterating over the pixels in a vector by the well-known // Bresenham rendering algorithm. // Starting with major/2 in the accumulator, on each step add major_step, // and then add minor to the accumulator. When the accumulator >= major // subtract major and step a minor step. void ICOORD::setup_render(ICOORD* major_step, ICOORD* minor_step, int* major, int* minor) const { int abs_x = abs(xcoord); int abs_y = abs(ycoord); if (abs_x >= abs_y) { // X-direction is major. major_step->xcoord = sign(xcoord); major_step->ycoord = 0; minor_step->xcoord = 0; minor_step->ycoord = sign(ycoord); *major = abs_x; *minor = abs_y; } else { // Y-direction is major. major_step->xcoord = 0; major_step->ycoord = sign(ycoord); minor_step->xcoord = sign(xcoord); minor_step->ycoord = 0; *major = abs_y; *minor = abs_x; } } // Returns the standard feature direction corresponding to this. // See binary_angle_plus_pi below for a description of the direction. uinT8 FCOORD::to_direction() const { return binary_angle_plus_pi(angle()); } // Sets this with a unit vector in the given standard feature direction. void FCOORD::from_direction(uinT8 direction) { double radians = angle_from_direction(direction); xcoord = cos(radians); ycoord = sin(radians); } // Converts an angle in radians (from ICOORD::angle or FCOORD::angle) to a // standard feature direction as an unsigned angle in 256ths of a circle // measured anticlockwise from (-1, 0). uinT8 FCOORD::binary_angle_plus_pi(double radians) { return Modulo(IntCastRounded((radians + M_PI) * 128.0 / M_PI), 256); } // Inverse of binary_angle_plus_pi returns an angle in radians for the // given standard feature direction. double FCOORD::angle_from_direction(uinT8 direction) { return direction * M_PI / 128.0 - M_PI; } // Returns the point on the given line nearest to this, ie the point such // that the vector point->this is perpendicular to the line. // The line is defined as a line_point and a dir_vector for its direction. FCOORD FCOORD::nearest_pt_on_line(const FCOORD& line_point, const FCOORD& dir_vector) const { FCOORD point_vector(*this - line_point); // The dot product (%) is |dir_vector||point_vector|cos theta, so dividing by // the square of the length of dir_vector gives us the fraction of dir_vector // to add to line1 to get the appropriate point, so // result = line1 + lambda dir_vector. double lambda = point_vector % dir_vector / dir_vector.sqlength(); return line_point + (dir_vector * lambda); } tesseract-3.04.01/ccstruct/points.h000066400000000000000000000223701266071204500172020ustar00rootroot00000000000000/********************************************************************** * File: points.h (Formerly coords.h) * Description: Coordinate class definitions. * Author: Ray Smith * Created: Fri Mar 15 08:32:45 GMT 1991 * * (C) Copyright 1991, 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 POINTS_H #define POINTS_H #include #include #include "elst.h" class FCOORD; ///integer coordinate class ICOORD { friend class FCOORD; public: ///empty constructor ICOORD() { xcoord = ycoord = 0; //default zero } ///constructor ///@param xin x value ///@param yin y value ICOORD(inT16 xin, inT16 yin) { xcoord = xin; ycoord = yin; } ///destructor ~ICOORD () { } ///access function inT16 x() const { return xcoord; } ///access_function inT16 y() const { return ycoord; } ///rewrite function void set_x(inT16 xin) { xcoord = xin; //write new value } ///rewrite function void set_y(inT16 yin) { //value to set ycoord = yin; } /// Set from the given x,y, shrinking the vector to fit if needed. void set_with_shrink(int x, int y); ///find sq length float sqlength() const { return (float) (xcoord * xcoord + ycoord * ycoord); } ///find length float length() const { return (float) sqrt (sqlength ()); } ///sq dist between pts float pt_to_pt_sqdist(const ICOORD &pt) const { ICOORD gap; gap.xcoord = xcoord - pt.xcoord; gap.ycoord = ycoord - pt.ycoord; return gap.sqlength (); } ///Distance between pts float pt_to_pt_dist(const ICOORD &pt) const { return (float) sqrt (pt_to_pt_sqdist (pt)); } ///find angle float angle() const { return (float) atan2 ((double) ycoord, (double) xcoord); } ///test equality BOOL8 operator== (const ICOORD & other) const { return xcoord == other.xcoord && ycoord == other.ycoord; } ///test inequality BOOL8 operator!= (const ICOORD & other) const { return xcoord != other.xcoord || ycoord != other.ycoord; } ///rotate 90 deg anti friend ICOORD operator! (const ICOORD &); ///unary minus friend ICOORD operator- (const ICOORD &); ///add friend ICOORD operator+ (const ICOORD &, const ICOORD &); ///add friend ICOORD & operator+= (ICOORD &, const ICOORD &); ///subtract friend ICOORD operator- (const ICOORD &, const ICOORD &); ///subtract friend ICOORD & operator-= (ICOORD &, const ICOORD &); ///scalar product friend inT32 operator% (const ICOORD &, const ICOORD &); ///cross product friend inT32 operator *(const ICOORD &, const ICOORD &); ///multiply friend ICOORD operator *(const ICOORD &, inT16); ///multiply friend ICOORD operator *(inT16, const ICOORD &); ///multiply friend ICOORD & operator*= (ICOORD &, inT16); ///divide friend ICOORD operator/ (const ICOORD &, inT16); ///divide friend ICOORD & operator/= (ICOORD &, inT16); ///rotate ///@param vec by vector void rotate(const FCOORD& vec); /// Setup for iterating over the pixels in a vector by the well-known /// Bresenham rendering algorithm. /// Starting with major/2 in the accumulator, on each step move by /// major_step, and then add minor to the accumulator. When /// accumulator >= major subtract major and also move by minor_step. void setup_render(ICOORD* major_step, ICOORD* minor_step, int* major, int* minor) const; // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); protected: inT16 xcoord; //< x value inT16 ycoord; //< y value }; class DLLSYM ICOORDELT:public ELIST_LINK, public ICOORD //embedded coord list { public: ///empty constructor ICOORDELT() { } ///constructor from ICOORD ICOORDELT (ICOORD icoord):ICOORD (icoord) { } ///constructor ///@param xin x value ///@param yin y value ICOORDELT(inT16 xin, inT16 yin) { xcoord = xin; ycoord = yin; } static ICOORDELT* deep_copy(const ICOORDELT* src) { ICOORDELT* elt = new ICOORDELT; *elt = *src; return elt; } }; ELISTIZEH (ICOORDELT) class DLLSYM FCOORD { public: ///empty constructor FCOORD() { } ///constructor ///@param xvalue x value ///@param yvalue y value FCOORD(float xvalue, float yvalue) { xcoord = xvalue; //set coords ycoord = yvalue; } FCOORD( //make from ICOORD ICOORD icoord) { //coords to set xcoord = icoord.xcoord; ycoord = icoord.ycoord; } float x() const { //get coords return xcoord; } float y() const { return ycoord; } ///rewrite function void set_x(float xin) { xcoord = xin; //write new value } ///rewrite function void set_y(float yin) { //value to set ycoord = yin; } ///find sq length float sqlength() const { return xcoord * xcoord + ycoord * ycoord; } ///find length float length() const { return (float) sqrt (sqlength ()); } ///sq dist between pts float pt_to_pt_sqdist(const FCOORD &pt) const { FCOORD gap; gap.xcoord = xcoord - pt.xcoord; gap.ycoord = ycoord - pt.ycoord; return gap.sqlength (); } ///Distance between pts float pt_to_pt_dist(const FCOORD &pt) const { return (float) sqrt (pt_to_pt_sqdist (pt)); } ///find angle float angle() const { return (float) atan2 (ycoord, xcoord); } // Returns the standard feature direction corresponding to this. // See binary_angle_plus_pi below for a description of the direction. uinT8 to_direction() const; // Sets this with a unit vector in the given standard feature direction. void from_direction(uinT8 direction); // Converts an angle in radians (from ICOORD::angle or FCOORD::angle) to a // standard feature direction as an unsigned angle in 256ths of a circle // measured anticlockwise from (-1, 0). static uinT8 binary_angle_plus_pi(double angle); // Inverse of binary_angle_plus_pi returns an angle in radians for the // given standard feature direction. static double angle_from_direction(uinT8 direction); // Returns the point on the given line nearest to this, ie the point such // that the vector point->this is perpendicular to the line. // The line is defined as a line_point and a dir_vector for its direction. // dir_vector need not be a unit vector. FCOORD nearest_pt_on_line(const FCOORD& line_point, const FCOORD& dir_vector) const; ///Convert to unit vec bool normalise(); ///test equality BOOL8 operator== (const FCOORD & other) { return xcoord == other.xcoord && ycoord == other.ycoord; } ///test inequality BOOL8 operator!= (const FCOORD & other) { return xcoord != other.xcoord || ycoord != other.ycoord; } ///rotate 90 deg anti friend FCOORD operator! (const FCOORD &); ///unary minus friend FCOORD operator- (const FCOORD &); ///add friend FCOORD operator+ (const FCOORD &, const FCOORD &); ///add friend FCOORD & operator+= (FCOORD &, const FCOORD &); ///subtract friend FCOORD operator- (const FCOORD &, const FCOORD &); ///subtract friend FCOORD & operator-= (FCOORD &, const FCOORD &); ///scalar product friend float operator% (const FCOORD &, const FCOORD &); ///cross product friend float operator *(const FCOORD &, const FCOORD &); ///multiply friend FCOORD operator *(const FCOORD &, float); ///multiply friend FCOORD operator *(float, const FCOORD &); ///multiply friend FCOORD & operator*= (FCOORD &, float); ///divide friend FCOORD operator/ (const FCOORD &, float); ///rotate ///@param vec by vector void rotate(const FCOORD vec); // unrotate - undo a rotate(vec) // @param vec by vector void unrotate(const FCOORD &vec); ///divide friend FCOORD & operator/= (FCOORD &, float); private: float xcoord; //2 floating coords float ycoord; }; #include "ipoints.h" /*do inline funcs */ #endif tesseract-3.04.01/ccstruct/polyaprx.cpp000066400000000000000000000472321266071204500201030ustar00rootroot00000000000000/********************************************************************** * File: polyaprx.cpp (Formerly polygon.c) * Description: Code for polygonal approximation from old edgeprog. * Author: Ray Smith * Created: Thu Nov 25 11:42:04 GMT 1993 * * (C) Copyright 1993, 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. * **********************************************************************/ #include #ifdef __UNIX__ #include #endif #define FASTEDGELENGTH 256 #include "polyaprx.h" #include "params.h" #include "tprintf.h" #define EXTERN EXTERN BOOL_VAR(poly_debug, FALSE, "Debug old poly"); EXTERN BOOL_VAR(poly_wide_objects_better, TRUE, "More accurate approx on wide things"); #define FIXED 4 /*OUTLINE point is fixed */ #define RUNLENGTH 1 /*length of run */ #define DIR 2 /*direction of run */ #define FLAGS 0 #define fixed_dist 20 //really an int_variable #define approx_dist 15 //really an int_variable const int par1 = 4500 / (approx_dist * approx_dist); const int par2 = 6750 / (approx_dist * approx_dist); /********************************************************************** * tesspoly_outline * * Approximate an outline from chain codes form using the old tess algorithm. * If allow_detailed_fx is true, the EDGEPTs in the returned TBLOB * contain pointers to the input C_OUTLINEs that enable higher-resolution * feature extraction that does not use the polygonal approximation. **********************************************************************/ TESSLINE* ApproximateOutline(bool allow_detailed_fx, C_OUTLINE* c_outline) { TBOX loop_box; // bounding box inT32 area; // loop area EDGEPT stack_edgepts[FASTEDGELENGTH]; // converted path EDGEPT* edgepts = stack_edgepts; // Use heap memory if the stack buffer is not big enough. if (c_outline->pathlength() > FASTEDGELENGTH) edgepts = new EDGEPT[c_outline->pathlength()]; loop_box = c_outline->bounding_box(); area = loop_box.height(); if (!poly_wide_objects_better && loop_box.width() > area) area = loop_box.width(); area *= area; edgesteps_to_edgepts(c_outline, edgepts); fix2(edgepts, area); EDGEPT* edgept = poly2(edgepts, area); // 2nd approximation. EDGEPT* startpt = edgept; EDGEPT* result = NULL; EDGEPT* prev_result = NULL; do { EDGEPT* new_pt = new EDGEPT; new_pt->pos = edgept->pos; new_pt->prev = prev_result; if (prev_result == NULL) { result = new_pt; } else { prev_result->next = new_pt; new_pt->prev = prev_result; } if (allow_detailed_fx) { new_pt->src_outline = edgept->src_outline; new_pt->start_step = edgept->start_step; new_pt->step_count = edgept->step_count; } prev_result = new_pt; edgept = edgept->next; } while (edgept != startpt); prev_result->next = result; result->prev = prev_result; if (edgepts != stack_edgepts) delete [] edgepts; return TESSLINE::BuildFromOutlineList(result); } /********************************************************************** * edgesteps_to_edgepts * * Convert a C_OUTLINE to EDGEPTs. **********************************************************************/ EDGEPT * edgesteps_to_edgepts ( //convert outline C_OUTLINE * c_outline, //input EDGEPT edgepts[] //output is array ) { inT32 length; //steps in path ICOORD pos; //current coords inT32 stepindex; //current step inT32 stepinc; //increment inT32 epindex; //current EDGEPT inT32 count; //repeated steps ICOORD vec; //for this 8 step ICOORD prev_vec; inT8 epdir; //of this step DIR128 prevdir; //prvious dir DIR128 dir; //of this step pos = c_outline->start_pos (); //start of loop length = c_outline->pathlength (); stepindex = 0; epindex = 0; prevdir = -1; count = 0; int prev_stepindex = 0; do { dir = c_outline->step_dir (stepindex); vec = c_outline->step (stepindex); if (stepindex < length - 1 && c_outline->step_dir (stepindex + 1) - dir == -32) { dir += 128 - 16; vec += c_outline->step (stepindex + 1); stepinc = 2; } else stepinc = 1; if (count == 0) { prevdir = dir; prev_vec = vec; } if (prevdir.get_dir () != dir.get_dir ()) { edgepts[epindex].pos.x = pos.x (); edgepts[epindex].pos.y = pos.y (); prev_vec *= count; edgepts[epindex].vec.x = prev_vec.x (); edgepts[epindex].vec.y = prev_vec.y (); pos += prev_vec; edgepts[epindex].flags[RUNLENGTH] = count; edgepts[epindex].prev = &edgepts[epindex - 1]; edgepts[epindex].flags[FLAGS] = 0; edgepts[epindex].next = &edgepts[epindex + 1]; prevdir += 64; epdir = (DIR128) 0 - prevdir; epdir >>= 4; epdir &= 7; edgepts[epindex].flags[DIR] = epdir; edgepts[epindex].src_outline = c_outline; edgepts[epindex].start_step = prev_stepindex; edgepts[epindex].step_count = stepindex - prev_stepindex; epindex++; prevdir = dir; prev_vec = vec; count = 1; prev_stepindex = stepindex; } else count++; stepindex += stepinc; } while (stepindex < length); edgepts[epindex].pos.x = pos.x (); edgepts[epindex].pos.y = pos.y (); prev_vec *= count; edgepts[epindex].vec.x = prev_vec.x (); edgepts[epindex].vec.y = prev_vec.y (); pos += prev_vec; edgepts[epindex].flags[RUNLENGTH] = count; edgepts[epindex].flags[FLAGS] = 0; edgepts[epindex].src_outline = c_outline; edgepts[epindex].start_step = prev_stepindex; edgepts[epindex].step_count = stepindex - prev_stepindex; edgepts[epindex].prev = &edgepts[epindex - 1]; edgepts[epindex].next = &edgepts[0]; prevdir += 64; epdir = (DIR128) 0 - prevdir; epdir >>= 4; epdir &= 7; edgepts[epindex].flags[DIR] = epdir; edgepts[0].prev = &edgepts[epindex]; ASSERT_HOST (pos.x () == c_outline->start_pos ().x () && pos.y () == c_outline->start_pos ().y ()); return &edgepts[0]; } /********************************************************************** *fix2(start,area) fixes points on the outline according to a trial method* **********************************************************************/ //#pragma OPT_LEVEL 1 /*stop compiler bugs*/ void fix2( //polygonal approx EDGEPT *start, /*loop to approimate */ int area) { EDGEPT *edgept; /*current point */ EDGEPT *edgept1; EDGEPT *loopstart; /*modified start of loop */ EDGEPT *linestart; /*start of line segment */ int dir1, dir2; /*directions of line */ int sum1, sum2; /*lengths in dir1,dir2 */ int stopped; /*completed flag */ int fixed_count; //no of fixed points int d01, d12, d23, gapmin; TPOINT d01vec, d12vec, d23vec; EDGEPT *edgefix, *startfix; EDGEPT *edgefix0, *edgefix1, *edgefix2, *edgefix3; edgept = start; /*start of loop */ while (((edgept->flags[DIR] - edgept->prev->flags[DIR] + 1) & 7) < 3 && (dir1 = (edgept->prev->flags[DIR] - edgept->next->flags[DIR]) & 7) != 2 && dir1 != 6) edgept = edgept->next; /*find suitable start */ loopstart = edgept; /*remember start */ stopped = 0; /*not finished yet */ edgept->flags[FLAGS] |= FIXED; /*fix it */ do { linestart = edgept; /*possible start of line */ dir1 = edgept->flags[DIR]; /*first direction */ /*length of dir1 */ sum1 = edgept->flags[RUNLENGTH]; edgept = edgept->next; dir2 = edgept->flags[DIR]; /*2nd direction */ /*length in dir2 */ sum2 = edgept->flags[RUNLENGTH]; if (((dir1 - dir2 + 1) & 7) < 3) { while (edgept->prev->flags[DIR] == edgept->next->flags[DIR]) { edgept = edgept->next; /*look at next */ if (edgept->flags[DIR] == dir1) /*sum lengths */ sum1 += edgept->flags[RUNLENGTH]; else sum2 += edgept->flags[RUNLENGTH]; } if (edgept == loopstart) stopped = 1; /*finished */ if (sum2 + sum1 > 2 && linestart->prev->flags[DIR] == dir2 && (linestart->prev->flags[RUNLENGTH] > linestart->flags[RUNLENGTH] || sum2 > sum1)) { /*start is back one */ linestart = linestart->prev; linestart->flags[FLAGS] |= FIXED; } if (((edgept->next->flags[DIR] - edgept->flags[DIR] + 1) & 7) >= 3 || (edgept->flags[DIR] == dir1 && sum1 >= sum2) || ((edgept->prev->flags[RUNLENGTH] < edgept->flags[RUNLENGTH] || (edgept->flags[DIR] == dir2 && sum2 >= sum1)) && linestart->next != edgept)) edgept = edgept->next; } /*sharp bend */ edgept->flags[FLAGS] |= FIXED; } /*do whole loop */ while (edgept != loopstart && !stopped); edgept = start; do { if (((edgept->flags[RUNLENGTH] >= 8) && (edgept->flags[DIR] != 2) && (edgept->flags[DIR] != 6)) || ((edgept->flags[RUNLENGTH] >= 8) && ((edgept->flags[DIR] == 2) || (edgept->flags[DIR] == 6)))) { edgept->flags[FLAGS] |= FIXED; edgept1 = edgept->next; edgept1->flags[FLAGS] |= FIXED; } edgept = edgept->next; } while (edgept != start); edgept = start; do { /*single fixed step */ if (edgept->flags[FLAGS] & FIXED && edgept->flags[RUNLENGTH] == 1 /*and neighours free */ && edgept->next->flags[FLAGS] & FIXED && (edgept->prev->flags[FLAGS] & FIXED) == 0 /*same pair of dirs */ && (edgept->next->next->flags[FLAGS] & FIXED) == 0 && edgept->prev->flags[DIR] == edgept->next->flags[DIR] && edgept->prev->prev->flags[DIR] == edgept->next->next->flags[DIR] && ((edgept->prev->flags[DIR] - edgept->flags[DIR] + 1) & 7) < 3) { /*unfix it */ edgept->flags[FLAGS] &= ~FIXED; edgept->next->flags[FLAGS] &= ~FIXED; } edgept = edgept->next; /*do all points */ } while (edgept != start); /*until finished */ stopped = 0; if (area < 450) area = 450; gapmin = area * fixed_dist * fixed_dist / 44000; edgept = start; fixed_count = 0; do { if (edgept->flags[FLAGS] & FIXED) fixed_count++; edgept = edgept->next; } while (edgept != start); while ((edgept->flags[FLAGS] & FIXED) == 0) edgept = edgept->next; edgefix0 = edgept; edgept = edgept->next; while ((edgept->flags[FLAGS] & FIXED) == 0) edgept = edgept->next; edgefix1 = edgept; edgept = edgept->next; while ((edgept->flags[FLAGS] & FIXED) == 0) edgept = edgept->next; edgefix2 = edgept; edgept = edgept->next; while ((edgept->flags[FLAGS] & FIXED) == 0) edgept = edgept->next; edgefix3 = edgept; startfix = edgefix2; do { if (fixed_count <= 3) break; //already too few point_diff (d12vec, edgefix1->pos, edgefix2->pos); d12 = LENGTH (d12vec); // TODO(rays) investigate this change: // Only unfix a point if it is part of a low-curvature section // of outline and the total angle change of the outlines is // less than 90 degrees, ie the scalar product is positive. // if (d12 <= gapmin && SCALAR(edgefix0->vec, edgefix2->vec) > 0) { if (d12 <= gapmin) { point_diff (d01vec, edgefix0->pos, edgefix1->pos); d01 = LENGTH (d01vec); point_diff (d23vec, edgefix2->pos, edgefix3->pos); d23 = LENGTH (d23vec); if (d01 > d23) { edgefix2->flags[FLAGS] &= ~FIXED; fixed_count--; } else { edgefix1->flags[FLAGS] &= ~FIXED; fixed_count--; edgefix1 = edgefix2; } } else { edgefix0 = edgefix1; edgefix1 = edgefix2; } edgefix2 = edgefix3; edgept = edgept->next; while ((edgept->flags[FLAGS] & FIXED) == 0) { if (edgept == startfix) stopped = 1; edgept = edgept->next; } edgefix3 = edgept; edgefix = edgefix2; } while ((edgefix != startfix) && (!stopped)); } //#pragma OPT_LEVEL 2 /*stop compiler bugs*/ /********************************************************************** *poly2(startpt,area,path) applies a second approximation to the outline *using the points which have been fixed by the first approximation* **********************************************************************/ EDGEPT *poly2( //second poly EDGEPT *startpt, /*start of loop */ int area /*area of blob box */ ) { EDGEPT *edgept; /*current outline point */ EDGEPT *loopstart; /*starting point */ EDGEPT *linestart; /*start of line */ int edgesum; /*correction count */ if (area < 1200) area = 1200; /*minimum value */ loopstart = NULL; /*not found it yet */ edgept = startpt; /*start of loop */ do { /*current point fixed */ if (edgept->flags[FLAGS] & FIXED /*and next not */ && (edgept->next->flags[FLAGS] & FIXED) == 0) { loopstart = edgept; /*start of repoly */ break; } edgept = edgept->next; /*next point */ } while (edgept != startpt); /*until found or finished */ if (loopstart == NULL && (startpt->flags[FLAGS] & FIXED) == 0) { /*fixed start of loop */ startpt->flags[FLAGS] |= FIXED; loopstart = startpt; /*or start of loop */ } if (loopstart) { do { edgept = loopstart; /*first to do */ do { linestart = edgept; edgesum = 0; /*sum of lengths */ do { /*sum lengths */ edgesum += edgept->flags[RUNLENGTH]; edgept = edgept->next; /*move on */ } while ((edgept->flags[FLAGS] & FIXED) == 0 && edgept != loopstart && edgesum < 126); if (poly_debug) tprintf ("Poly2:starting at (%d,%d)+%d=(%d,%d),%d to (%d,%d)\n", linestart->pos.x, linestart->pos.y, linestart->flags[DIR], linestart->vec.x, linestart->vec.y, edgesum, edgept->pos.x, edgept->pos.y); /*reapproximate */ cutline(linestart, edgept, area); while ((edgept->next->flags[FLAGS] & FIXED) && edgept != loopstart) edgept = edgept->next; /*look for next non-fixed */ } /*do all the loop */ while (edgept != loopstart); edgesum = 0; do { if (edgept->flags[FLAGS] & FIXED) edgesum++; edgept = edgept->next; } //count fixed pts while (edgept != loopstart); if (edgesum < 3) area /= 2; //must have 3 pts } while (edgesum < 3); do { linestart = edgept; do { edgept = edgept->next; } while ((edgept->flags[FLAGS] & FIXED) == 0); linestart->next = edgept; edgept->prev = linestart; linestart->vec.x = edgept->pos.x - linestart->pos.x; linestart->vec.y = edgept->pos.y - linestart->pos.y; } while (edgept != loopstart); } else edgept = startpt; /*start of loop */ loopstart = edgept; /*new start */ return loopstart; /*correct exit */ } /********************************************************************** *cutline(first,last,area) straightens out a line by partitioning *and joining the ends by a straight line* **********************************************************************/ void cutline( //recursive refine EDGEPT *first, /*ends of line */ EDGEPT *last, int area /*area of object */ ) { EDGEPT *edge; /*current edge */ TPOINT vecsum; /*vector sum */ int vlen; /*approx length of vecsum */ TPOINT vec; /*accumulated vector */ EDGEPT *maxpoint; /*worst point */ int maxperp; /*max deviation */ int perp; /*perp distance */ int ptcount; /*no of points */ int squaresum; /*sum of perps */ edge = first; /*start of line */ if (edge->next == last) return; /*simple line */ /*vector sum */ vecsum.x = last->pos.x - edge->pos.x; vecsum.y = last->pos.y - edge->pos.y; if (vecsum.x == 0 && vecsum.y == 0) { /*special case */ vecsum.x = -edge->prev->vec.x; vecsum.y = -edge->prev->vec.y; } /*absolute value */ vlen = vecsum.x > 0 ? vecsum.x : -vecsum.x; if (vecsum.y > vlen) vlen = vecsum.y; /*maximum */ else if (-vecsum.y > vlen) vlen = -vecsum.y; /*absolute value */ vec.x = edge->vec.x; /*accumulated vector */ vec.y = edge->vec.y; maxperp = 0; /*none yet */ squaresum = ptcount = 0; edge = edge->next; /*move to actual point */ maxpoint = edge; /*in case there isn't one */ do { perp = CROSS (vec, vecsum); /*get perp distance */ if (perp != 0) { perp *= perp; /*squared deviation */ } squaresum += perp; /*sum squares */ ptcount++; /*count points */ if (poly_debug) tprintf ("Cutline:Final perp=%d\n", perp); if (perp > maxperp) { maxperp = perp; maxpoint = edge; /*find greatest deviation */ } vec.x += edge->vec.x; /*accumulate vectors */ vec.y += edge->vec.y; edge = edge->next; } while (edge != last); /*test all line */ perp = LENGTH (vecsum); ASSERT_HOST (perp != 0); if (maxperp < 256 * MAX_INT16) { maxperp <<= 8; maxperp /= perp; /*true max perp */ } else { maxperp /= perp; maxperp <<= 8; /*avoid overflow */ } if (squaresum < 256 * MAX_INT16) /*mean squared perp */ perp = (squaresum << 8) / (perp * ptcount); else /*avoid overflow */ perp = (squaresum / perp << 8) / ptcount; if (poly_debug) tprintf ("Cutline:A=%d, max=%.2f(%.2f%%), msd=%.2f(%.2f%%)\n", area, maxperp / 256.0, maxperp * 200.0 / area, perp / 256.0, perp * 300.0 / area); if (maxperp * par1 >= 10 * area || perp * par2 >= 10 * area || vlen >= 126) { maxpoint->flags[FLAGS] |= FIXED; /*partitions */ cutline(first, maxpoint, area); cutline(maxpoint, last, area); } } tesseract-3.04.01/ccstruct/polyaprx.h000066400000000000000000000034221266071204500175410ustar00rootroot00000000000000/********************************************************************** * File: polyaprx.h (Formerly polygon.h) * Description: Code for polygonal approximation from old edgeprog. * Author: Ray Smith * Created: Thu Nov 25 11:42:04 GMT 1993 * * (C) Copyright 1993, 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 POLYAPRX_H #define POLYAPRX_H #include "blobs.h" #include "coutln.h" // convert a chain-coded input to the old OUTLINE approximation TESSLINE* ApproximateOutline(bool allow_detailed_fx, C_OUTLINE *c_outline); EDGEPT *edgesteps_to_edgepts ( //convert outline C_OUTLINE * c_outline, //input EDGEPT edgepts[] //output is array ); void fix2( //polygonal approx EDGEPT *start, /*loop to approimate */ int area); EDGEPT *poly2( //second poly EDGEPT *startpt, /*start of loop */ int area /*area of blob box */ ); void cutline( //recursive refine EDGEPT *first, /*ends of line */ EDGEPT *last, int area /*area of object */ ); #endif tesseract-3.04.01/ccstruct/polyblk.cpp000066400000000000000000000272221266071204500176760ustar00rootroot00000000000000/********************************************************************** * File: polyblk.c (Formerly poly_block.c) * Description: Polygonal blocks * Author: Sheelagh Lloyd? * Created: * * (C) Copyright 1993, 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. * **********************************************************************/ #include #include #include #include "elst.h" #include "polyblk.h" // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #define PBLOCK_LABEL_SIZE 150 #define INTERSECTING MAX_INT16 int lessthan(const void *first, const void *second); POLY_BLOCK::POLY_BLOCK(ICOORDELT_LIST *points, PolyBlockType t) { ICOORDELT_IT v = &vertices; vertices.clear(); v.move_to_first(); v.add_list_before(points); compute_bb(); type = t; } // Initialize from box coordinates. POLY_BLOCK::POLY_BLOCK(const TBOX& box, PolyBlockType t) { vertices.clear(); ICOORDELT_IT v = &vertices; v.move_to_first(); v.add_to_end(new ICOORDELT(box.left(), box.top())); v.add_to_end(new ICOORDELT(box.left(), box.bottom())); v.add_to_end(new ICOORDELT(box.right(), box.bottom())); v.add_to_end(new ICOORDELT(box.right(), box.top())); compute_bb(); type = t; } /** * @name POLY_BLOCK::compute_bb * * Compute the bounding box from the outline points. */ void POLY_BLOCK::compute_bb() { //constructor ICOORD ibl, itr; //integer bb ICOORD botleft; //bounding box ICOORD topright; ICOORD pos; //current pos; ICOORDELT_IT pts = &vertices; //iterator botleft = *pts.data (); topright = botleft; do { pos = *pts.data (); if (pos.x () < botleft.x ()) //get bounding box botleft = ICOORD (pos.x (), botleft.y ()); if (pos.y () < botleft.y ()) botleft = ICOORD (botleft.x (), pos.y ()); if (pos.x () > topright.x ()) topright = ICOORD (pos.x (), topright.y ()); if (pos.y () > topright.y ()) topright = ICOORD (topright.x (), pos.y ()); pts.forward (); } while (!pts.at_first ()); ibl = ICOORD (botleft.x (), botleft.y ()); itr = ICOORD (topright.x (), topright.y ()); box = TBOX (ibl, itr); } /** * @name POLY_BLOCK::winding_number * * Return the winding number of the outline around the given point. * @param point point to wind around */ inT16 POLY_BLOCK::winding_number(const ICOORD &point) { inT16 count; //winding count ICOORD pt; //current point ICOORD vec; //point to current point ICOORD vvec; //current point to next point inT32 cross; //cross product ICOORDELT_IT it = &vertices; //iterator count = 0; do { pt = *it.data (); vec = pt - point; vvec = *it.data_relative (1) - pt; //crossing the line if (vec.y () <= 0 && vec.y () + vvec.y () > 0) { cross = vec * vvec; //cross product if (cross > 0) count++; //crossing right half else if (cross == 0) return INTERSECTING; //going through point } else if (vec.y () > 0 && vec.y () + vvec.y () <= 0) { cross = vec * vvec; if (cross < 0) count--; //crossing back else if (cross == 0) return INTERSECTING; //illegal } else if (vec.y () == 0 && vec.x () == 0) return INTERSECTING; it.forward (); } while (!it.at_first ()); return count; //winding number } /// @return true if other is inside this. bool POLY_BLOCK::contains(POLY_BLOCK *other) { inT16 count; // winding count ICOORDELT_IT it = &vertices; // iterator ICOORD vertex; if (!box.overlap (*(other->bounding_box ()))) return false; // can't be contained /* check that no vertex of this is inside other */ do { vertex = *it.data (); // get winding number count = other->winding_number (vertex); if (count != INTERSECTING) if (count != 0) return false; it.forward (); } while (!it.at_first ()); /* check that all vertices of other are inside this */ //switch lists it.set_to_list (other->points ()); do { vertex = *it.data (); //try other way round count = winding_number (vertex); if (count != INTERSECTING) if (count == 0) return false; it.forward (); } while (!it.at_first ()); return true; } /** * @name POLY_BLOCK::rotate * * Rotate the POLY_BLOCK. * @param rotation cos, sin of angle */ void POLY_BLOCK::rotate(FCOORD rotation) { FCOORD pos; //current pos; ICOORDELT *pt; //current point ICOORDELT_IT pts = &vertices; //iterator do { pt = pts.data (); pos.set_x (pt->x ()); pos.set_y (pt->y ()); pos.rotate (rotation); pt->set_x ((inT16) (floor (pos.x () + 0.5))); pt->set_y ((inT16) (floor (pos.y () + 0.5))); pts.forward (); } while (!pts.at_first ()); compute_bb(); } /** * @name POLY_BLOCK::reflect_in_y_axis * * Reflect the coords of the polygon in the y-axis. (Flip the sign of x.) */ void POLY_BLOCK::reflect_in_y_axis() { ICOORDELT *pt; // current point ICOORDELT_IT pts = &vertices; // Iterator. do { pt = pts.data(); pt->set_x(-pt->x()); pts.forward(); } while (!pts.at_first()); compute_bb(); } /** * POLY_BLOCK::move * * Move the POLY_BLOCK. * @param shift x,y translation vector */ void POLY_BLOCK::move(ICOORD shift) { ICOORDELT *pt; //current point ICOORDELT_IT pts = &vertices; //iterator do { pt = pts.data (); *pt += shift; pts.forward (); } while (!pts.at_first ()); compute_bb(); } #ifndef GRAPHICS_DISABLED void POLY_BLOCK::plot(ScrollView* window, inT32 num) { ICOORDELT_IT v = &vertices; window->Pen(ColorForPolyBlockType(type)); v.move_to_first (); if (num > 0) { window->TextAttributes("Times", 80, false, false, false); char temp_buff[34]; #if defined(__UNIX__) || defined(MINGW) sprintf(temp_buff, INT32FORMAT, num); #else ltoa (num, temp_buff, 10); #endif window->Text(v.data ()->x (), v.data ()->y (), temp_buff); } window->SetCursor(v.data ()->x (), v.data ()->y ()); for (v.mark_cycle_pt (); !v.cycled_list (); v.forward ()) { window->DrawTo(v.data ()->x (), v.data ()->y ()); } v.move_to_first (); window->DrawTo(v.data ()->x (), v.data ()->y ()); } void POLY_BLOCK::fill(ScrollView* window, ScrollView::Color colour) { inT16 y; inT16 width; PB_LINE_IT *lines; ICOORDELT_LIST *segments; ICOORDELT_IT s_it; lines = new PB_LINE_IT (this); window->Pen(colour); for (y = this->bounding_box ()->bottom (); y <= this->bounding_box ()->top (); y++) { segments = lines->get_line (y); if (!segments->empty ()) { s_it.set_to_list (segments); for (s_it.mark_cycle_pt (); !s_it.cycled_list (); s_it.forward ()) { // Note different use of ICOORDELT, x coord is x coord of pixel // at the start of line segment, y coord is length of line segment // Last pixel is start pixel + length. width = s_it.data ()->y (); window->SetCursor(s_it.data ()->x (), y); window->DrawTo(s_it.data ()->x () + (float) width, y); } } } } #endif /// @return true if the polygons of other and this overlap. bool POLY_BLOCK::overlap(POLY_BLOCK *other) { inT16 count; // winding count ICOORDELT_IT it = &vertices; // iterator ICOORD vertex; if (!box.overlap(*(other->bounding_box()))) return false; // can't be any overlap. /* see if a vertex of this is inside other */ do { vertex = *it.data (); // get winding number count = other->winding_number (vertex); if (count != INTERSECTING) if (count != 0) return true; it.forward (); } while (!it.at_first ()); /* see if a vertex of other is inside this */ // switch lists it.set_to_list (other->points ()); do { vertex = *it.data(); // try other way round count = winding_number (vertex); if (count != INTERSECTING) if (count != 0) return true; it.forward (); } while (!it.at_first ()); return false; } ICOORDELT_LIST *PB_LINE_IT::get_line(inT16 y) { ICOORDELT_IT v, r; ICOORDELT_LIST *result; ICOORDELT *x, *current, *previous; float fy, fx; fy = (float) (y + 0.5); result = new ICOORDELT_LIST (); r.set_to_list (result); v.set_to_list (block->points ()); for (v.mark_cycle_pt (); !v.cycled_list (); v.forward ()) { if (((v.data_relative (-1)->y () > y) && (v.data ()->y () <= y)) || ((v.data_relative (-1)->y () <= y) && (v.data ()->y () > y))) { previous = v.data_relative (-1); current = v.data (); fx = (float) (0.5 + previous->x () + (current->x () - previous->x ()) * (fy - previous->y ()) / (current->y () - previous->y ())); x = new ICOORDELT ((inT16) fx, 0); r.add_to_end (x); } } if (!r.empty ()) { r.sort (lessthan); for (r.mark_cycle_pt (); !r.cycled_list (); r.forward ()) x = r.data (); for (r.mark_cycle_pt (); !r.cycled_list (); r.forward ()) { r.data ()->set_y (r.data_relative (1)->x () - r.data ()->x ()); r.forward (); delete (r.extract ()); } } return result; } int lessthan(const void *first, const void *second) { ICOORDELT *p1 = (*(ICOORDELT **) first); ICOORDELT *p2 = (*(ICOORDELT **) second); if (p1->x () < p2->x ()) return (-1); else if (p1->x () > p2->x ()) return (1); else return (0); } #ifndef GRAPHICS_DISABLED /// Returns a color to draw the given type. ScrollView::Color POLY_BLOCK::ColorForPolyBlockType(PolyBlockType type) { // Keep kPBColors in sync with PolyBlockType. const ScrollView::Color kPBColors[PT_COUNT] = { ScrollView::WHITE, // Type is not yet known. Keep as the 1st element. ScrollView::BLUE, // Text that lives inside a column. ScrollView::CYAN, // Text that spans more than one column. ScrollView::MEDIUM_BLUE, // Text that is in a cross-column pull-out region. ScrollView::AQUAMARINE, // Partition belonging to an equation region. ScrollView::SKY_BLUE, // Partition belonging to an inline equation region. ScrollView::MAGENTA, // Partition belonging to a table region. ScrollView::GREEN, // Text-line runs vertically. ScrollView::LIGHT_BLUE, // Text that belongs to an image. ScrollView::RED, // Image that lives inside a column. ScrollView::YELLOW, // Image that spans more than one column. ScrollView::ORANGE, // Image in a cross-column pull-out region. ScrollView::BROWN, // Horizontal Line. ScrollView::DARK_GREEN, // Vertical Line. ScrollView::GREY // Lies outside of any column. }; if (type >= 0 && type < PT_COUNT) { return kPBColors[type]; } return ScrollView::WHITE; } #endif // GRAPHICS_DISABLED tesseract-3.04.01/ccstruct/polyblk.h000066400000000000000000000063411266071204500173420ustar00rootroot00000000000000/********************************************************************** * File: polyblk.h (Formerly poly_block.h) * Description: Polygonal blocks * Author: Sheelagh Lloyd? * Created: * * (C) Copyright 1993, 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 POLYBLK_H #define POLYBLK_H #include "publictypes.h" #include "elst.h" #include "points.h" #include "rect.h" #include "scrollview.h" class DLLSYM POLY_BLOCK { public: POLY_BLOCK() { } // Initialize from box coordinates. POLY_BLOCK(const TBOX& box, PolyBlockType type); POLY_BLOCK(ICOORDELT_LIST *points, PolyBlockType type); ~POLY_BLOCK () { } TBOX *bounding_box() { // access function return &box; } ICOORDELT_LIST *points() { // access function return &vertices; } void compute_bb(); PolyBlockType isA() const { return type; } bool IsText() const { return PTIsTextType(type); } // Rotate about the origin by the given rotation. (Analogous to // multiplying by a complex number. void rotate(FCOORD rotation); // Reflect the coords of the polygon in the y-axis. (Flip the sign of x.) void reflect_in_y_axis(); // Move by adding shift to all coordinates. void move(ICOORD shift); void plot(ScrollView* window, inT32 num); #ifndef GRAPHICS_DISABLED void fill(ScrollView* window, ScrollView::Color colour); #endif // GRAPHICS_DISABLED // Returns true if other is inside this. bool contains(POLY_BLOCK *other); // Returns true if the polygons of other and this overlap. bool overlap(POLY_BLOCK *other); // Returns the winding number of this around the test_pt. // Positive for anticlockwise, negative for clockwise, and zero for // test_pt outside this. inT16 winding_number(const ICOORD &test_pt); #ifndef GRAPHICS_DISABLED // Static utility functions to handle the PolyBlockType. // Returns a color to draw the given type. static ScrollView::Color ColorForPolyBlockType(PolyBlockType type); #endif // GRAPHICS_DISABLED private: ICOORDELT_LIST vertices; // vertices TBOX box; // bounding box PolyBlockType type; // Type of this region. }; // Class to iterate the scanlines of a polygon. class DLLSYM PB_LINE_IT { public: PB_LINE_IT(POLY_BLOCK *blkptr) { block = blkptr; } void set_to_block(POLY_BLOCK * blkptr) { block = blkptr; } // Returns a list of runs of pixels for the given y coord. // Each element of the returned list is the start (x) and extent(y) of // a run inside the region. // Delete the returned list after use. ICOORDELT_LIST *get_line(inT16 y); private: POLY_BLOCK * block; }; #endif tesseract-3.04.01/ccstruct/publictypes.cpp000066400000000000000000000024701266071204500205630ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: publictypes.cpp // Description: Types used in both the API and internally // Author: Ray Smith // Created: Wed Mar 03 11:17:09 PST 2010 // // (C) Copyright 2010, 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 "publictypes.h" /** String name for each block type. Keep in sync with PolyBlockType. */ const char* kPolyBlockNames[] = { "Unknown", "Flowing Text", "Heading Text", "Pullout Text", "Equation", "Inline Equation", "Table", "Vertical Text", "Caption Text", "Flowing Image", "Heading Image", "Pullout Image", "Horizontal Line", "Vertical Line", "Noise", "" // End marker for testing that sizes match. }; tesseract-3.04.01/ccstruct/publictypes.h000066400000000000000000000266761266071204500202460ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: publictypes.h // Description: Types used in both the API and internally // Author: Ray Smith // Created: Wed Mar 03 09:22:53 PST 2010 // // (C) Copyright 2010, 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_PUBLICTYPES_H__ #define TESSERACT_CCSTRUCT_PUBLICTYPES_H__ // This file contains types that are used both by the API and internally // to Tesseract. In order to decouple the API from Tesseract and prevent cyclic // dependencies, THIS FILE SHOULD NOT DEPEND ON ANY OTHER PART OF TESSERACT. // Restated: It is OK for low-level Tesseract files to include publictypes.h, // but not for the low-level tesseract code to include top-level API code. // This file should not use other Tesseract types, as that would drag // their includes into the API-level. // API-level code should include apitypes.h in preference to this file. /** Number of printers' points in an inch. The unit of the pointsize return. */ const int kPointsPerInch = 72; /** * Possible types for a POLY_BLOCK or ColPartition. * Must be kept in sync with kPBColors in polyblk.cpp and PTIs*Type functions * below, as well as kPolyBlockNames in publictypes.cpp. * Used extensively by ColPartition, and POLY_BLOCK. */ enum PolyBlockType { PT_UNKNOWN, // Type is not yet known. Keep as the first element. PT_FLOWING_TEXT, // Text that lives inside a column. PT_HEADING_TEXT, // Text that spans more than one column. PT_PULLOUT_TEXT, // Text that is in a cross-column pull-out region. PT_EQUATION, // Partition belonging to an equation region. PT_INLINE_EQUATION, // Partition has inline equation. PT_TABLE, // Partition belonging to a table region. PT_VERTICAL_TEXT, // Text-line runs vertically. PT_CAPTION_TEXT, // Text that belongs to an image. PT_FLOWING_IMAGE, // Image that lives inside a column. PT_HEADING_IMAGE, // Image that spans more than one column. PT_PULLOUT_IMAGE, // Image that is in a cross-column pull-out region. PT_HORZ_LINE, // Horizontal Line. PT_VERT_LINE, // Vertical Line. PT_NOISE, // Lies outside of any column. PT_COUNT }; /** Returns true if PolyBlockType is of horizontal line type */ inline bool PTIsLineType(PolyBlockType type) { return type == PT_HORZ_LINE || type == PT_VERT_LINE; } /** Returns true if PolyBlockType is of image type */ inline bool PTIsImageType(PolyBlockType type) { return type == PT_FLOWING_IMAGE || type == PT_HEADING_IMAGE || type == PT_PULLOUT_IMAGE; } /** Returns true if PolyBlockType is of text type */ inline bool PTIsTextType(PolyBlockType type) { return type == PT_FLOWING_TEXT || type == PT_HEADING_TEXT || type == PT_PULLOUT_TEXT || type == PT_TABLE || type == PT_VERTICAL_TEXT || type == PT_CAPTION_TEXT || type == PT_INLINE_EQUATION; } // Returns true if PolyBlockType is of pullout(inter-column) type inline bool PTIsPulloutType(PolyBlockType type) { return type == PT_PULLOUT_IMAGE || type == PT_PULLOUT_TEXT; } /** String name for each block type. Keep in sync with PolyBlockType. */ extern const char* kPolyBlockNames[]; namespace tesseract { /** * +------------------+ Orientation Example: * | 1 Aaaa Aaaa Aaaa | ==================== * | Aaa aa aaa aa | To left is a diagram of some (1) English and * | aaaaaa A aa aaa. | (2) Chinese text and a (3) photo credit. * | 2 | * | ####### c c C | Upright Latin characters are represented as A and a. * | ####### c c c | '<' represents a latin character rotated * | < ####### c c c | anti-clockwise 90 degrees. * | < ####### c c | * | < ####### . c | Upright Chinese characters are represented C and c. * | 3 ####### c | * +------------------+ NOTA BENE: enum values here should match goodoc.proto * If you orient your head so that "up" aligns with Orientation, * then the characters will appear "right side up" and readable. * * In the example above, both the English and Chinese paragraphs are oriented * so their "up" is the top of the page (page up). The photo credit is read * with one's head turned leftward ("up" is to page left). * * The values of this enum match the convention of Tesseract's osdetect.h */ enum Orientation { ORIENTATION_PAGE_UP = 0, ORIENTATION_PAGE_RIGHT = 1, ORIENTATION_PAGE_DOWN = 2, ORIENTATION_PAGE_LEFT = 3, }; /** * The grapheme clusters within a line of text are laid out logically * in this direction, judged when looking at the text line rotated so that * its Orientation is "page up". * * For English text, the writing direction is left-to-right. For the * Chinese text in the above example, the writing direction is top-to-bottom. */ enum WritingDirection { WRITING_DIRECTION_LEFT_TO_RIGHT = 0, WRITING_DIRECTION_RIGHT_TO_LEFT = 1, WRITING_DIRECTION_TOP_TO_BOTTOM = 2, }; /** * The text lines are read in the given sequence. * * In English, the order is top-to-bottom. * In Chinese, vertical text lines are read right-to-left. Mongolian is * written in vertical columns top to bottom like Chinese, but the lines * order left-to right. * * Note that only some combinations make sense. For example, * WRITING_DIRECTION_LEFT_TO_RIGHT implies TEXTLINE_ORDER_TOP_TO_BOTTOM */ enum TextlineOrder { TEXTLINE_ORDER_LEFT_TO_RIGHT = 0, TEXTLINE_ORDER_RIGHT_TO_LEFT = 1, TEXTLINE_ORDER_TOP_TO_BOTTOM = 2, }; /** * Possible modes for page layout analysis. These *must* be kept in order * of decreasing amount of layout analysis to be done, except for OSD_ONLY, * so that the inequality test macros below work. */ enum PageSegMode { PSM_OSD_ONLY, ///< Orientation and script detection only. PSM_AUTO_OSD, ///< Automatic page segmentation with orientation and ///< script detection. (OSD) PSM_AUTO_ONLY, ///< Automatic page segmentation, but no OSD, or OCR. PSM_AUTO, ///< Fully automatic page segmentation, but no OSD. PSM_SINGLE_COLUMN, ///< Assume a single column of text of variable sizes. PSM_SINGLE_BLOCK_VERT_TEXT, ///< Assume a single uniform block of vertically ///< aligned text. PSM_SINGLE_BLOCK, ///< Assume a single uniform block of text. (Default.) PSM_SINGLE_LINE, ///< Treat the image as a single text line. PSM_SINGLE_WORD, ///< Treat the image as a single word. PSM_CIRCLE_WORD, ///< Treat the image as a single word in a circle. PSM_SINGLE_CHAR, ///< Treat the image as a single character. PSM_SPARSE_TEXT, ///< Find as much text as possible in no particular order. PSM_SPARSE_TEXT_OSD, ///< Sparse text with orientation and script det. PSM_RAW_LINE, ///< Treat the image as a single text line, bypassing ///< hacks that are Tesseract-specific. PSM_COUNT ///< Number of enum entries. }; /** * Inline functions that act on a PageSegMode to determine whether components of * layout analysis are enabled. * *Depend critically on the order of elements of PageSegMode.* * NOTE that arg is an int for compatibility with INT_PARAM. */ inline bool PSM_OSD_ENABLED(int pageseg_mode) { return pageseg_mode <= PSM_AUTO_OSD || pageseg_mode == PSM_SPARSE_TEXT_OSD; } inline bool PSM_ORIENTATION_ENABLED(int pageseg_mode) { return pageseg_mode <= PSM_AUTO || pageseg_mode == PSM_SPARSE_TEXT_OSD; } inline bool PSM_COL_FIND_ENABLED(int pageseg_mode) { return pageseg_mode >= PSM_AUTO_OSD && pageseg_mode <= PSM_AUTO; } inline bool PSM_SPARSE(int pageseg_mode) { return pageseg_mode == PSM_SPARSE_TEXT || pageseg_mode == PSM_SPARSE_TEXT_OSD; } inline bool PSM_BLOCK_FIND_ENABLED(int pageseg_mode) { return pageseg_mode >= PSM_AUTO_OSD && pageseg_mode <= PSM_SINGLE_COLUMN; } inline bool PSM_LINE_FIND_ENABLED(int pageseg_mode) { return pageseg_mode >= PSM_AUTO_OSD && pageseg_mode <= PSM_SINGLE_BLOCK; } inline bool PSM_WORD_FIND_ENABLED(int pageseg_mode) { return (pageseg_mode >= PSM_AUTO_OSD && pageseg_mode <= PSM_SINGLE_LINE) || pageseg_mode == PSM_SPARSE_TEXT || pageseg_mode == PSM_SPARSE_TEXT_OSD; } /** * enum of the elements of the page hierarchy, used in ResultIterator * to provide functions that operate on each level without having to * have 5x as many functions. */ enum PageIteratorLevel { RIL_BLOCK, // Block of text/image/separator line. RIL_PARA, // Paragraph within a block. RIL_TEXTLINE, // Line within a paragraph. RIL_WORD, // Word within a textline. RIL_SYMBOL // Symbol/character within a word. }; /** * JUSTIFICATION_UNKNONW * The alignment is not clearly one of the other options. This could happen * for example if there are only one or two lines of text or the text looks * like source code or poetry. * * NOTA BENE: Fully justified paragraphs (text aligned to both left and right * margins) are marked by Tesseract with JUSTIFICATION_LEFT if their text * is written with a left-to-right script and with JUSTIFICATION_RIGHT if * their text is written in a right-to-left script. * * Interpretation for text read in vertical lines: * "Left" is wherever the starting reading position is. * * JUSTIFICATION_LEFT * Each line, except possibly the first, is flush to the same left tab stop. * * JUSTIFICATION_CENTER * The text lines of the paragraph are centered about a line going * down through their middle of the text lines. * * JUSTIFICATION_RIGHT * Each line, except possibly the first, is flush to the same right tab stop. */ enum ParagraphJustification { JUSTIFICATION_UNKNOWN, JUSTIFICATION_LEFT, JUSTIFICATION_CENTER, JUSTIFICATION_RIGHT, }; /** * When Tesseract/Cube is initialized we can choose to instantiate/load/run * only the Tesseract part, only the Cube part or both along with the combiner. * The preference of which engine to use is stored in tessedit_ocr_engine_mode. * * ATTENTION: When modifying this enum, please make sure to make the * appropriate changes to all the enums mirroring it (e.g. OCREngine in * cityblock/workflow/detection/detection_storage.proto). Such enums will * mention the connection to OcrEngineMode in the comments. */ enum OcrEngineMode { OEM_TESSERACT_ONLY, // Run Tesseract only - fastest OEM_CUBE_ONLY, // Run Cube only - better accuracy, but slower OEM_TESSERACT_CUBE_COMBINED, // Run both and combine results - best accuracy OEM_DEFAULT // Specify this mode when calling init_*(), // to indicate that any of the above modes // should be automatically inferred from the // variables in the language-specific config, // command-line configs, or if not specified // in any of the above should be set to the // default OEM_TESSERACT_ONLY. }; } // namespace tesseract. #endif // TESSERACT_CCSTRUCT_PUBLICTYPES_H__ tesseract-3.04.01/ccstruct/quadlsq.cpp000066400000000000000000000111471266071204500176730ustar00rootroot00000000000000/********************************************************************** * File: quadlsq.cpp (Formerly qlsq.c) * Description: Code for least squares approximation of quadratics. * Author: Ray Smith * Created: Wed Oct 6 15:14:23 BST 1993 * * (C) Copyright 1993, 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. * **********************************************************************/ #include #include #include "quadlsq.h" #include "tprintf.h" // Minimum variance in least squares before backing off to a lower degree. const double kMinVariance = 1.0 / 1024; /********************************************************************** * QLSQ::clear * * Function to initialize a QLSQ. **********************************************************************/ void QLSQ::clear() { // initialize a = 0.0; b = 0.0; c = 0.0; n = 0; // No elements. sigx = 0.0; // Zero accumulators. sigy = 0.0; sigxx = 0.0; sigxy = 0.0; sigyy = 0.0; sigxxx = 0.0; sigxxy = 0.0; sigxxxx = 0.0; } /********************************************************************** * QLSQ::add * * Add an element to the accumulator. **********************************************************************/ void QLSQ::add(double x, double y) { n++; // Count elements. sigx += x; // Update accumulators. sigy += y; sigxx += x * x; sigxy += x * y; sigyy += y * y; sigxxx += static_cast(x) * x * x; sigxxy += static_cast(x) * x * y; sigxxxx += static_cast(x) * x * x * x; } /********************************************************************** * QLSQ::remove * * Delete an element from the accumulator. **********************************************************************/ void QLSQ::remove(double x, double y) { if (n <= 0) { tprintf("Can't remove an element from an empty QLSQ accumulator!\n"); return; } n--; // Count elements. sigx -= x; // Update accumulators. sigy -= y; sigxx -= x * x; sigxy -= x * y; sigyy -= y * y; sigxxx -= static_cast(x) * x * x; sigxxy -= static_cast(x) * x * y; sigxxxx -= static_cast(x) * x * x * x; } /********************************************************************** * QLSQ::fit * * Fit the given degree of polynomial and store the result. * This creates a quadratic of the form axx + bx + c, but limited to * the given degree. **********************************************************************/ void QLSQ::fit(int degree) { long double x_variance = static_cast(sigxx) * n - static_cast(sigx) * sigx; // Note: for computational efficiency, we do not normalize the variance, // covariance and cube variance here as they are in the same order in both // nominators and denominators. However, we need be careful in value range // check. if (x_variance < kMinVariance * n * n || degree < 1 || n < 2) { // We cannot calculate b reliably so forget a and b, and just work on c. a = b = 0.0; if (n >= 1 && degree >= 0) { c = sigy / n; } else { c = 0.0; } return; } long double top96 = 0.0; // Accurate top. long double bottom96 = 0.0; // Accurate bottom. long double cubevar = sigxxx * n - static_cast(sigxx) * sigx; long double covariance = static_cast(sigxy) * n - static_cast(sigx) * sigy; if (n >= 4 && degree >= 2) { top96 = cubevar * covariance; top96 += x_variance * (static_cast(sigxx) * sigy - sigxxy * n); bottom96 = cubevar * cubevar; bottom96 -= x_variance * (sigxxxx * n - static_cast(sigxx) * sigxx); } if (bottom96 >= kMinVariance * n * n * n * n) { // Denominators looking good a = top96 / bottom96; top96 = covariance - cubevar * a; b = top96 / x_variance; } else { // Forget a, and concentrate on b. a = 0.0; b = covariance / x_variance; } c = (sigy - a * sigxx - b * sigx) / n; } tesseract-3.04.01/ccstruct/quadlsq.h000066400000000000000000000041701266071204500173360ustar00rootroot00000000000000/********************************************************************** * File: quadlsq.h (Formerly qlsq.h) * Description: Code for least squares approximation of quadratics. * Author: Ray Smith * Created: Wed Oct 6 15:14:23 BST 1993 * * (C) Copyright 1993, 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 QUADLSQ_H #define QUADLSQ_H #include "points.h" class QLSQ { public: QLSQ() { //constructor clear(); //set to zeros } void clear(); //initialize void add( //add element double x, //coords to add double y); void remove( //delete element double x, //coords to delete double y); inT32 count() { //no of elements return n; } void fit( //fit the given int degree); //return actual double get_a() { //get x squard return a; } double get_b() { //get x squard return b; } double get_c() { //get x squard return c; } private: inT32 n; //no of elements double a, b, c; //result double sigx; //sum of x double sigy; //sum of y double sigxx; //sum x squared double sigxy; //sum of xy double sigyy; //sum y squared long double sigxxx; //sum x cubed long double sigxxy; //sum xsquared y long double sigxxxx; //sum x fourth }; #endif tesseract-3.04.01/ccstruct/quadratc.h000066400000000000000000000040601266071204500174660ustar00rootroot00000000000000/********************************************************************** * File: quadratc.h (Formerly quadrtic.h) * Description: Code for the QUAD_COEFFS class. * Author: Ray Smith * Created: Tue Oct 08 17:24:40 BST 1991 * * (C) Copyright 1991, 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 QUADRATC_H #define QUADRATC_H #include "points.h" class QUAD_COEFFS { public: QUAD_COEFFS() { } //empty constructor QUAD_COEFFS( //constructor double xsq, //coefficients float x, float constant) { a = xsq; b = x; c = constant; } float y( //evaluate float x) const { //at x return (float) ((a * x + b) * x + c); } void move( // reposition word ICOORD vec) { // by vector /************************************************************ y - q = a (x - p)^2 + b (x - p) + c y - q = ax^2 - 2apx + ap^2 + bx - bp + c y = ax^2 + (b - 2ap)x + (c - bp + ap^2 + q) ************************************************************/ inT16 p = vec.x (); inT16 q = vec.y (); c = (float) (c - b * p + a * p * p + q); b = (float) (b - 2 * a * p); } double a; //x squared float b; //x float c; //constant private: }; #endif tesseract-3.04.01/ccstruct/quspline.cpp000066400000000000000000000334331266071204500200630ustar00rootroot00000000000000/********************************************************************** * File: quspline.cpp (Formerly qspline.c) * Description: Code for the QSPLINE class. * Author: Ray Smith * Created: Tue Oct 08 17:16:12 BST 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include "allheaders.h" #include "memry.h" #include "quadlsq.h" #include "quspline.h" // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #define QSPLINE_PRECISION 16 //no of steps to draw /********************************************************************** * QSPLINE::QSPLINE * * Constructor to build a QSPLINE given the components used in the old code. **********************************************************************/ QSPLINE::QSPLINE( //constructor inT32 count, //no of segments inT32 *xstarts, //start coords double *coeffs //coefficients ) { inT32 index; //segment index //get memory xcoords = (inT32 *) alloc_mem ((count + 1) * sizeof (inT32)); quadratics = (QUAD_COEFFS *) alloc_mem (count * sizeof (QUAD_COEFFS)); segments = count; for (index = 0; index < segments; index++) { //copy them xcoords[index] = xstarts[index]; quadratics[index] = QUAD_COEFFS (coeffs[index * 3], coeffs[index * 3 + 1], coeffs[index * 3 + 2]); } //right edge xcoords[index] = xstarts[index]; } /********************************************************************** * QSPLINE::QSPLINE * * Constructor to build a QSPLINE by appproximation of points. **********************************************************************/ QSPLINE::QSPLINE ( //constructor int xstarts[], //spline boundaries int segcount, //no of segments int xpts[], //points to fit int ypts[], int pointcount, //no of pts int degree //fit required ) { int pointindex; /*no along text line */ int segment; /*segment no */ inT32 *ptcounts; //no in each segment QLSQ qlsq; /*accumulator */ segments = segcount; xcoords = (inT32 *) alloc_mem ((segcount + 1) * sizeof (inT32)); ptcounts = (inT32 *) alloc_mem ((segcount + 1) * sizeof (inT32)); quadratics = (QUAD_COEFFS *) alloc_mem (segcount * sizeof (QUAD_COEFFS)); memmove (xcoords, xstarts, (segcount + 1) * sizeof (inT32)); ptcounts[0] = 0; /*none in any yet */ for (segment = 0, pointindex = 0; pointindex < pointcount; pointindex++) { while (segment < segcount && xpts[pointindex] >= xstarts[segment]) { segment++; /*try next segment */ /*cumulative counts */ ptcounts[segment] = ptcounts[segment - 1]; } ptcounts[segment]++; /*no in previous partition */ } while (segment < segcount) { segment++; /*zero the rest */ ptcounts[segment] = ptcounts[segment - 1]; } for (segment = 0; segment < segcount; segment++) { qlsq.clear (); /*first blob */ pointindex = ptcounts[segment]; if (pointindex > 0 && xpts[pointindex] != xpts[pointindex - 1] && xpts[pointindex] != xstarts[segment]) qlsq.add (xstarts[segment], ypts[pointindex - 1] + (ypts[pointindex] - ypts[pointindex - 1]) * (xstarts[segment] - xpts[pointindex - 1]) / (xpts[pointindex] - xpts[pointindex - 1])); for (; pointindex < ptcounts[segment + 1]; pointindex++) { qlsq.add (xpts[pointindex], ypts[pointindex]); } if (pointindex > 0 && pointindex < pointcount && xpts[pointindex] != xstarts[segment + 1]) qlsq.add (xstarts[segment + 1], ypts[pointindex - 1] + (ypts[pointindex] - ypts[pointindex - 1]) * (xstarts[segment + 1] - xpts[pointindex - 1]) / (xpts[pointindex] - xpts[pointindex - 1])); qlsq.fit (degree); quadratics[segment].a = qlsq.get_a (); quadratics[segment].b = qlsq.get_b (); quadratics[segment].c = qlsq.get_c (); } free_mem(ptcounts); } /********************************************************************** * QSPLINE::QSPLINE * * Constructor to build a QSPLINE from another. **********************************************************************/ QSPLINE::QSPLINE( //constructor const QSPLINE &src) { segments = 0; xcoords = NULL; quadratics = NULL; *this = src; } /********************************************************************** * QSPLINE::~QSPLINE * * Destroy a QSPLINE. **********************************************************************/ QSPLINE::~QSPLINE ( //constructor ) { if (xcoords != NULL) { free_mem(xcoords); xcoords = NULL; } if (quadratics != NULL) { free_mem(quadratics); quadratics = NULL; } } /********************************************************************** * QSPLINE::operator= * * Copy a QSPLINE **********************************************************************/ QSPLINE & QSPLINE::operator= ( //assignment const QSPLINE & source) { if (xcoords != NULL) free_mem(xcoords); if (quadratics != NULL) free_mem(quadratics); segments = source.segments; xcoords = (inT32 *) alloc_mem ((segments + 1) * sizeof (inT32)); quadratics = (QUAD_COEFFS *) alloc_mem (segments * sizeof (QUAD_COEFFS)); memmove (xcoords, source.xcoords, (segments + 1) * sizeof (inT32)); memmove (quadratics, source.quadratics, segments * sizeof (QUAD_COEFFS)); return *this; } /********************************************************************** * QSPLINE::step * * Return the total of the step functions between the given coords. **********************************************************************/ double QSPLINE::step( //find step functions double x1, //between coords double x2) { int index1, index2; //indices of coords double total; /*total steps */ index1 = spline_index (x1); index2 = spline_index (x2); total = 0; while (index1 < index2) { total += (double) quadratics[index1 + 1].y ((float) xcoords[index1 + 1]); total -= (double) quadratics[index1].y ((float) xcoords[index1 + 1]); index1++; /*next segment */ } return total; /*total steps */ } /********************************************************************** * QSPLINE::y * * Return the y value at the given x value. **********************************************************************/ double QSPLINE::y( //evaluate double x //coord to evaluate at ) const { inT32 index; //segment index index = spline_index (x); return quadratics[index].y (x);//in correct segment } /********************************************************************** * QSPLINE::spline_index * * Return the index to the largest xcoord not greater than x. **********************************************************************/ inT32 QSPLINE::spline_index( //evaluate double x //coord to evaluate at ) const { inT32 index; //segment index inT32 bottom; //bottom of range inT32 top; //top of range bottom = 0; top = segments; while (top - bottom > 1) { index = (top + bottom) / 2; //centre of range if (x >= xcoords[index]) bottom = index; //new min else top = index; //new max } return bottom; } /********************************************************************** * QSPLINE::move * * Reposition spline by vector **********************************************************************/ void QSPLINE::move( // reposition spline ICOORD vec // by vector ) { inT32 segment; //index of segment inT16 x_shift = vec.x (); for (segment = 0; segment < segments; segment++) { xcoords[segment] += x_shift; quadratics[segment].move (vec); } xcoords[segment] += x_shift; } /********************************************************************** * QSPLINE::overlap * * Return TRUE if spline2 overlaps this by no more than fraction less * than the bounds of this. **********************************************************************/ BOOL8 QSPLINE::overlap( //test overlap QSPLINE *spline2, //2 cannot be smaller double fraction //by more than this ) { int leftlimit; /*common left limit */ int rightlimit; /*common right limit */ leftlimit = xcoords[1]; rightlimit = xcoords[segments - 1]; /*or too non-overlap */ if (spline2->segments < 3 || spline2->xcoords[1] > leftlimit + fraction * (rightlimit - leftlimit) || spline2->xcoords[spline2->segments - 1] < rightlimit - fraction * (rightlimit - leftlimit)) return FALSE; else return TRUE; } /********************************************************************** * extrapolate_spline * * Extrapolates the spline linearly using the same gradient as the * quadratic has at either end. **********************************************************************/ void QSPLINE::extrapolate( //linear extrapolation double gradient, //gradient to use int xmin, //new left edge int xmax //new right edge ) { int segment; /*current segment of spline */ int dest_segment; //dest index int *xstarts; //new boundaries QUAD_COEFFS *quads; //new ones int increment; //in size increment = xmin < xcoords[0] ? 1 : 0; if (xmax > xcoords[segments]) increment++; if (increment == 0) return; xstarts = (int *) alloc_mem ((segments + 1 + increment) * sizeof (int)); quads = (QUAD_COEFFS *) alloc_mem ((segments + increment) * sizeof (QUAD_COEFFS)); if (xmin < xcoords[0]) { xstarts[0] = xmin; quads[0].a = 0; quads[0].b = gradient; quads[0].c = y (xcoords[0]) - quads[0].b * xcoords[0]; dest_segment = 1; } else dest_segment = 0; for (segment = 0; segment < segments; segment++) { xstarts[dest_segment] = xcoords[segment]; quads[dest_segment] = quadratics[segment]; dest_segment++; } xstarts[dest_segment] = xcoords[segment]; if (xmax > xcoords[segments]) { quads[dest_segment].a = 0; quads[dest_segment].b = gradient; quads[dest_segment].c = y (xcoords[segments]) - quads[dest_segment].b * xcoords[segments]; dest_segment++; xstarts[dest_segment] = xmax + 1; } segments = dest_segment; free_mem(xcoords); free_mem(quadratics); xcoords = (inT32 *) xstarts; quadratics = quads; } /********************************************************************** * QSPLINE::plot * * Draw the QSPLINE in the given colour. **********************************************************************/ #ifndef GRAPHICS_DISABLED void QSPLINE::plot( //draw it ScrollView* window, //window to draw in ScrollView::Color colour //colour to draw in ) const { inT32 segment; //index of segment inT16 step; //index of poly piece double increment; //x increment double x; //x coord window->Pen(colour); for (segment = 0; segment < segments; segment++) { increment = (double) (xcoords[segment + 1] - xcoords[segment]) / QSPLINE_PRECISION; x = xcoords[segment]; for (step = 0; step <= QSPLINE_PRECISION; step++) { if (segment == 0 && step == 0) window->SetCursor(x, quadratics[segment].y (x)); else window->DrawTo(x, quadratics[segment].y (x)); x += increment; } } } #endif void QSPLINE::plot(Pix *pix) const { if (pix == NULL) { return; } inT32 segment; // Index of segment inT16 step; // Index of poly piece double increment; // x increment double x; // x coord double height = static_cast(pixGetHeight(pix)); Pta* points = ptaCreate(QSPLINE_PRECISION * segments); const int kLineWidth = 5; for (segment = 0; segment < segments; segment++) { increment = static_cast((xcoords[segment + 1] - xcoords[segment])) / QSPLINE_PRECISION; x = xcoords[segment]; for (step = 0; step <= QSPLINE_PRECISION; step++) { double y = height - quadratics[segment].y(x); ptaAddPt(points, x, y); x += increment; } } switch (pixGetDepth(pix)) { case 1: pixRenderPolyline(pix, points, kLineWidth, L_SET_PIXELS, 1); break; case 32: pixRenderPolylineArb(pix, points, kLineWidth, 255, 0, 0, 1); break; default: pixRenderPolyline(pix, points, kLineWidth, L_CLEAR_PIXELS, 1); break; } ptaDestroy(&points); } tesseract-3.04.01/ccstruct/quspline.h000066400000000000000000000071711266071204500175300ustar00rootroot00000000000000/********************************************************************** * File: quspline.h (Formerly qspline.h) * Description: Code for the QSPLINE class. * Author: Ray Smith * Created: Tue Oct 08 17:16:12 BST 1991 * * (C) Copyright 1991, 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 QUSPLINE_H #define QUSPLINE_H #include "quadratc.h" #include "serialis.h" #include "memry.h" #include "rect.h" class ROW; struct Pix; class QSPLINE { friend void make_first_baseline(TBOX *, int, int *, int *, QSPLINE *, QSPLINE *, float); friend void make_holed_baseline(TBOX *, int, QSPLINE *, QSPLINE *, float); friend void tweak_row_baseline(ROW *, double, double); public: QSPLINE() { //empty constructor segments = 0; xcoords = NULL; //everything empty quadratics = NULL; } QSPLINE( //copy constructor const QSPLINE &src); QSPLINE( //constructor inT32 count, //number of segments inT32 *xstarts, //segment starts double *coeffs); //coefficients ~QSPLINE (); //destructor QSPLINE ( //least squares fit int xstarts[], //spline boundaries int segcount, //no of segments int xcoords[], //points to fit int ycoords[], int blobcount,//no of coords int degree); //function double step( //step change double x1, //between coords double x2); double y( //evaluate double x) const; //at x void move( // reposition spline ICOORD vec); // by vector BOOL8 overlap( //test overlap QSPLINE *spline2, //2 cannot be smaller double fraction); //by more than this void extrapolate( //linear extrapolation double gradient, //gradient to use int left, //new left edge int right); //new right edge #ifndef GRAPHICS_DISABLED void plot( //draw it ScrollView* window, //in window ScrollView::Color colour) const; //in colour #endif // Paint the baseline over pix. If pix has depth of 32, then the line will // be painted in red. Otherwise it will be painted in black. void plot(Pix* pix) const; QSPLINE & operator= ( const QSPLINE & source); //from this private: inT32 spline_index( //binary search double x) const; //for x inT32 segments; //no of segments inT32 *xcoords; //no of coords QUAD_COEFFS *quadratics; //spline pieces }; #endif tesseract-3.04.01/ccstruct/ratngs.cpp000066400000000000000000000666071266071204500175320ustar00rootroot00000000000000/********************************************************************** * File: ratngs.cpp (Formerly ratings.c) * Description: Code to manipulate the BLOB_CHOICE and WERD_CHOICE classes. * Author: Ray Smith * Created: Thu Apr 23 13:23:29 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. * **********************************************************************/ #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "ratngs.h" #include "blobs.h" #include "callcpp.h" #include "genericvector.h" #include "matrix.h" #include "normalis.h" // kBlnBaselineOffset. #include "unicharset.h" 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"; static const char kPermuterTypeTopPerm[] = "Top Choice"; static const char kPermuterTypeLowerPerm[] = "Top Lower Case"; static const char kPermuterTypeUpperPerm[] = "Top Upper Case"; static const char kPermuterTypeNgramPerm[] = "Ngram"; static const char kPermuterTypeNumberPerm[] = "Number"; static const char kPermuterTypeUserPatPerm[] = "User Pattern"; static const char kPermuterTypeSysDawgPerm[] = "System Dictionary"; static const char kPermuterTypeDocDawgPerm[] = "Document Dictionary"; static const char kPermuterTypeUserDawgPerm[] = "User Dictionary"; static const char kPermuterTypeFreqDawgPerm[] = "Frequent Words Dictionary"; static const char kPermuterTypeCompoundPerm[] = "Compound"; static const char * const kPermuterTypeNames[] = { kPermuterTypeNoPerm, // 0 kPermuterTypePuncPerm, // 1 kPermuterTypeTopPerm, // 2 kPermuterTypeLowerPerm, // 3 kPermuterTypeUpperPerm, // 4 kPermuterTypeNgramPerm, // 5 kPermuterTypeNumberPerm, // 6 kPermuterTypeUserPatPerm, // 7 kPermuterTypeSysDawgPerm, // 8 kPermuterTypeDocDawgPerm, // 9 kPermuterTypeUserDawgPerm, // 10 kPermuterTypeFreqDawgPerm, // 11 kPermuterTypeCompoundPerm // 12 }; /** * BLOB_CHOICE::BLOB_CHOICE * * Constructor to build a BLOB_CHOICE from a char, rating and certainty. */ BLOB_CHOICE::BLOB_CHOICE(UNICHAR_ID src_unichar_id, // character id float src_rating, // rating float src_cert, // certainty int src_script_id, // script 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_ = -1; fontinfo_id2_ = -1; script_id_ = src_script_id; min_xheight_ = min_xheight; max_xheight_ = max_xheight; yshift_ = yshift; classifier_ = c; } /** * BLOB_CHOICE::BLOB_CHOICE * * Constructor to build a BLOB_CHOICE from another BLOB_CHOICE. */ BLOB_CHOICE::BLOB_CHOICE(const BLOB_CHOICE &other) : ELIST_LINK(other) { unichar_id_ = other.unichar_id(); rating_ = other.rating(); certainty_ = other.certainty(); fontinfo_id_ = other.fontinfo_id(); fontinfo_id2_ = other.fontinfo_id2(); script_id_ = other.script_id(); matrix_cell_ = other.matrix_cell_; min_xheight_ = other.min_xheight_; max_xheight_ = other.max_xheight_; yshift_ = other.yshift(); classifier_ = other.classifier_; fonts_ = other.fonts_; } // 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 * * Constructor to build a WERD_CHOICE from the given string. * The function assumes that src_string is not NULL. */ WERD_CHOICE::WERD_CHOICE(const char *src_string, const UNICHARSET &unicharset) : unicharset_(&unicharset){ 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(); } } /** * WERD_CHOICE::init * * Helper function to build a WERD_CHOICE from the given string, * fragment lengths, rating, certainty and permuter. * * The function assumes that src_string is not NULL. * src_lengths argument could be NULL, in which case the unichars * in src_string are assumed to all be of length 1. */ void WERD_CHOICE::init(const char *src_string, const char *src_lengths, float src_rating, float src_certainty, uinT8 src_permuter) { int src_string_len = strlen(src_string); if (src_string_len == 0) { this->init(8); } else { this->init(src_lengths ? strlen(src_lengths): src_string_len); length_ = reserved_; int offset = 0; for (int i = 0; i < length_; ++i) { int unichar_length = src_lengths ? src_lengths[i] : 1; unichar_ids_[i] = unicharset_->unichar_to_id(src_string+offset, unichar_length); 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; } /** * WERD_CHOICE::~WERD_CHOICE */ WERD_CHOICE::~WERD_CHOICE() { delete[] unichar_ids_; delete[] script_pos_; delete[] state_; delete[] certainties_; } const char *WERD_CHOICE::permuter_name() const { return kPermuterTypeNames[permuter_]; } // 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(); } /** * contains_unichar_id * * Returns true if unichar_ids_ contain the given unichar_id, false otherwise. */ bool WERD_CHOICE::contains_unichar_id(UNICHAR_ID unichar_id) const { for (int i = 0; i < length_; ++i) { if (unichar_ids_[i] == unichar_id) { return true; } } return false; } /** * remove_unichar_ids * * Removes num unichar ids starting from index start from unichar_ids_ * and updates length_ and fragment_lengths_ to reflect this change. * Note: this function does not modify rating_ and certainty_. */ void WERD_CHOICE::remove_unichar_ids(int start, int num) { ASSERT_HOST(start >= 0 && start + num <= length_); // 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; } /** * reverse_and_mirror_unichar_ids * * Reverses and mirrors unichars in unichar_ids. */ void WERD_CHOICE::reverse_and_mirror_unichar_ids() { 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); } if (length_ % 2 != 0) { unichar_ids_[length_/2] = unicharset_->get_mirror(unichar_ids_[length_/2]); } } /** * punct_stripped * * Returns the half-open interval of unichar_id indices [start, end) which * enclose the core portion of this word -- the part after stripping * punctuation from the left and right. */ void WERD_CHOICE::punct_stripped(int *start, int *end) const { *start = 0; *end = length() - 1; while (*start < length() && unicharset()->get_ispunctuation(unichar_id(*start))) { (*start)++; } while (*end > -1 && unicharset()->get_ispunctuation(unichar_id(*end))) { (*end)--; } (*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_); if (end < start) { end = start; } WERD_CHOICE retval(unicharset_, end - start); for (int i = start; i < end; i++) { retval.append_unichar_id_space_allocated( unichar_ids_[i], state_[i], 0.0f, certainties_[i]); } return retval; } /** * has_rtl_unichar_id * * Returns true if unichar_ids contain at least one "strongly" RTL unichar. */ bool WERD_CHOICE::has_rtl_unichar_id() const { int i; for (i = 0; i < length_; ++i) { UNICHARSET::Direction dir = unicharset_->get_direction(unichar_ids_[i]); if (dir == UNICHARSET::U_RIGHT_TO_LEFT || dir == UNICHARSET::U_RIGHT_TO_LEFT_ARABIC) { return true; } } return false; } /** * string_and_lengths * * Populates the given word_str with unichars from unichar_ids and * and word_lengths_str with the corresponding unichar lengths. */ void WERD_CHOICE::string_and_lengths(STRING *word_str, STRING *word_lengths_str) const { *word_str = ""; if (word_lengths_str != NULL) *word_lengths_str = ""; for (int i = 0; i < length_; ++i) { const char *ch = unicharset_->id_to_unichar_ext(unichar_ids_[i]); *word_str += ch; if (word_lengths_str != NULL) { *word_lengths_str += strlen(ch); } } } /** * append_unichar_id * * Make sure there is enough space in the word for the new unichar id * and call append_unichar_id_space_allocated(). */ void WERD_CHOICE::append_unichar_id( 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, blob_count, rating, certainty); } /** * WERD_CHOICE::operator+= * * Cat a second word rating on the end of this current one. * The ratings are added and the confidence is the min. * If the permuters are NOT the same the permuter is set to COMPOUND_PERM */ WERD_CHOICE & WERD_CHOICE::operator+= (const WERD_CHOICE & second) { ASSERT_HOST(unicharset_ == second.unicharset_); while (reserved_ < length_ + second.length()) { this->double_the_size(); } const UNICHAR_ID *other_unichar_ids = second.unichar_ids(); for (int i = 0; i < second.length(); ++i) { unichar_ids_[length_ + i] = other_unichar_ids[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; } return *this; } /** * WERD_CHOICE::operator= * * Allocate enough memory to hold a copy of source and copy over * all the information from source to this WERD_CHOICE. */ WERD_CHOICE& WERD_CHOICE::operator=(const WERD_CHOICE& source) { while (reserved_ < source.length()) { this->double_the_size(); } unicharset_ = source.unicharset_; const UNICHAR_ID *other_unichar_ids = source.unichar_ids(); for (int i = 0; i < source.length(); ++i) { unichar_ids_[i] = other_unichar_ids[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(); dangerous_ambig_found_ = source.dangerous_ambig_found_; return *this; } // 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() || word->NumBlobs() != TotalOfStates()) { return; } 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; } } } 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; } /* 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; } // 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; } // 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; } } } // 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; } /** * WERD_CHOICE::print * * Print WERD_CHOICE to stdout. */ 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(" : 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("\nstr"); for (int i = 0; i < length_; ++i) { tprintf("\t%s", unicharset_->id_to_unichar(unichar_ids_[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"); } // 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(); if (word2.unicharset() != uchset) return false; int w1start, w1end; word1.punct_stripped(&w1start, &w1end); int w2start, w2end; word2.punct_stripped(&w2start, &w2end); if (w1end - w1start != w2end - w2start) return false; for (int i = 0; i < w1end - w1start; i++) { if (uchset->to_lower(word1.unichar_id(w1start + i)) != uchset->to_lower(word2.unichar_id(w2start + i))) { return false; } } return true; } /** * print_ratings_list * * Send all the ratings out to the logfile. * * @param msg intro message * @param ratings list of ratings * @param current_unicharset unicharset that can be used * for id-to-unichar conversion */ void print_ratings_list(const char *msg, BLOB_CHOICE_LIST *ratings, const UNICHARSET ¤t_unicharset) { 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(¤t_unicharset); if (!c_it.at_last()) tprintf("\n"); } tprintf("\n"); fflush(stdout); } tesseract-3.04.01/ccstruct/ratngs.h000066400000000000000000000550351266071204500171700ustar00rootroot00000000000000/********************************************************************** * File: ratngs.h (Formerly ratings.h) * Description: Definition of the WERD_CHOICE and BLOB_CHOICE classes. * Author: Ray Smith * Created: Thu Apr 23 11:40:38 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 RATNGS_H #define RATNGS_H #include #include "clst.h" #include "elst.h" #include "fontinfo.h" #include "genericvector.h" #include "matrix.h" #include "unichar.h" #include "unicharset.h" #include "werd.h" class MATRIX; struct TBLOB; struct 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: BLOB_CHOICE() { unichar_id_ = UNICHAR_SPACE; fontinfo_id_ = -1; fontinfo_id2_ = -1; rating_ = 10.0; certainty_ = -1.0; script_id_ = -1; 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 int script_id, // script 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() {} UNICHAR_ID unichar_id() const { return unichar_id_; } float rating() const { return rating_; } float certainty() const { return certainty_; } inT16 fontinfo_id() const { return fontinfo_id_; } inT16 fontinfo_id2() const { return fontinfo_id2_; } const GenericVector& fonts() const { return fonts_; } void set_fonts(const GenericVector& fonts) { fonts_ = fonts; int score1 = 0, score2 = 0; fontinfo_id_ = -1; fontinfo_id2_ = -1; for (int f = 0; f < fonts_.size(); ++f) { if (fonts_[f].score > score1) { score2 = score1; fontinfo_id2_ = fontinfo_id_; score1 = fonts_[f].score; fontinfo_id_ = fonts_[f].fontinfo_id; } else if (fonts_[f].score > score2) { score2 = fonts_[f].score; fontinfo_id2_ = fonts_[f].fontinfo_id; } } } int script_id() const { return script_id_; } const MATRIX_COORD& matrix_cell() { return matrix_cell_; } inT16 xgap_before() const { return xgap_before_; } inT16 xgap_after() const { return xgap_after_; } float min_xheight() const { return min_xheight_; } float max_xheight() const { return max_xheight_; } 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) { unichar_id_ = newunichar_id; } void set_rating(float newrat) { rating_ = newrat; } void set_certainty(float newrat) { certainty_ = newrat; } void set_script(int newscript_id) { script_id_ = newscript_id; } 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; } void set_xgap_after(inT16 gap) { xgap_after_ = gap; } 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; } // 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 // Fonts and scores. Allowed to be empty. GenericVector fonts_; inT16 fontinfo_id_; // char font information inT16 fontinfo_id2_; // 2nd choice font information // Rating is the classifier distance weighted by the length of the outline // in the blob. In terms of probability, classifier distance is -klog p such // that the resulting distance is in the range [0, 1] and then // rating = w (-k log p) where w is the weight for the length of the outline. // Sums of ratings may be compared meaningfully for words of different // segmentation. float rating_; // size related // Certainty is a number in [-20, 0] indicating the classifier certainty // of the choice. In terms of probability, certainty = 20 (k log p) where // k is defined as above to normalize -klog p to the range [0, 1]. float certainty_; // absolute int script_id_; // 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. 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) // 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 { NO_PERM, // 0 PUNC_PERM, // 1 TOP_CHOICE_PERM, // 2 LOWER_CASE_PERM, // 3 UPPER_CASE_PERM, // 4 NGRAM_PERM, // 5 NUMBER_PERM, // 6 USER_PATTERN_PERM, // 7 SYSTEM_DAWG_PERM, // 8 DOC_DAWG_PERM, // 9 USER_DAWG_PERM, // 10 FREQ_DAWG_PERM, // 11 COMPOUND_PERM, // 12 NUM_PERMUTER_TYPES }; 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); } WERD_CHOICE(const UNICHARSET *unicharset, int reserved) : unicharset_(unicharset) { this->init(reserved); } WERD_CHOICE(const char *src_string, const char *src_lengths, float src_rating, float src_certainty, uinT8 src_permuter, const UNICHARSET &unicharset) : unicharset_(&unicharset) { this->init(src_string, src_lengths, src_rating, src_certainty, src_permuter); } WERD_CHOICE(const char *src_string, const UNICHARSET &unicharset); WERD_CHOICE(const WERD_CHOICE &word) : ELIST_LINK(word), unicharset_(word.unicharset_) { this->init(word.length()); this->operator=(word); } ~WERD_CHOICE(); const UNICHARSET *unicharset() const { return unicharset_; } 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_; } inline UNICHAR_ID unichar_id(int index) const { assert(index < length_); return unichar_ids_[index]; } inline int state(int index) const { return state_[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_; } 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; // 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; } 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; } inline void set_certainty(float new_val) { certainty_ = new_val; } inline void set_permuter(uinT8 perm) { permuter_ = perm; } // Note: this function should only be used if all the fields // are populated manually with set_* functions (rather than // (copy)constructors and append_* functions). inline void set_length(int len) { ASSERT_HOST(reserved_ >= len); length_ = len; } /// 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_); 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]; script_pos_ = new tesseract::ScriptPos[1]; state_ = new int[1]; certainties_ = new float[1]; reserved_ = 1; } } /// Initializes WERD_CHOICE - reserves length slots in unichar_ids_ and /// fragment_length_ arrays. Sets other values to default (blank) values. inline void init(int reserved) { reserved_ = reserved; if (reserved > 0) { unichar_ids_ = new UNICHAR_ID[reserved]; script_pos_ = new tesseract::ScriptPos[reserved]; state_ = new int[reserved]; certainties_ = new float[reserved]; } else { unichar_ids_ = 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; 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, /// fragment lengths, rating, certainty and permuter. /// The function assumes that src_string is not NULL. /// src_lengths argument could be NULL, in which case the unichars /// in src_string are assumed to all be of length 1. void init(const char *src_string, const char *src_lengths, float src_rating, float src_certainty, uinT8 src_permuter); /// Set the fields in this choice to be default (bad) values. inline void make_bad() { length_ = 0; rating_ = kBadRating; certainty_ = -MAX_FLOAT32; } /// 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, int blob_count, float rating, float certainty) { assert(reserved_ > length_); length_++; this->set_unichar_id(unichar_id, blob_count, rating, certainty, length_-1); } void append_unichar_id(UNICHAR_ID unichar_id, int blob_count, float rating, float certainty); 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; 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); inline void remove_last_unichar_id() { --length_; } inline void remove_unichar_id(int index) { this->remove_unichar_ids(index, 1); } bool has_rtl_unichar_id() const; void reverse_and_mirror_unichar_ids(); // Returns the half-open interval of unichar_id indices [start, end) which // enclose the core portion of this word -- the part after stripping // 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; void string_and_lengths(STRING *word_str, STRING *word_lengths_str) const; const STRING debug_string() const { STRING word_str; for (int i = 0; i < length_; ++i) { word_str += unicharset_->debug_str(unichar_ids_[i]); word_str += " "; } return word_str; } // Call this to override the default (strict left to right graphemes) // with the fact that some engine produces a "reading order" set of // Graphemes for each word. bool set_unichars_in_script_order(bool in_script_order) { return unichars_in_script_order_ = in_script_order; } bool unichars_in_script_order() const { return unichars_in_script_order_; } // Returns a UTF-8 string equivalent to the current choice // of UNICHAR IDs. const STRING &unichar_string() const { this->string_and_lengths(&unichar_string_, &unichar_lengths_); return unichar_string_; } // Returns the lengths, one byte each, representing the number of bytes // required in the unichar_string for each UNICHAR_ID. const STRING &unichar_lengths() const { this->string_and_lengths(&unichar_string_, &unichar_lengths_); return unichar_lengths_; } // 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 WERD_CHOICE& operator= (const WERD_CHOICE& source); 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 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 // 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_; }; // Make WERD_CHOICE listable. ELISTIZEH(WERD_CHOICE) typedef GenericVector BLOB_CHOICE_LIST_VECTOR; // Utilities for comparing WERD_CHOICEs bool EqualIgnoringCaseAndTerminalPunct(const WERD_CHOICE &word1, const WERD_CHOICE &word2); // Utilities for debug printing. 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 ); #endif tesseract-3.04.01/ccstruct/rect.cpp000066400000000000000000000176711266071204500171660ustar00rootroot00000000000000/********************************************************************** * File: rect.c (Formerly box.c) * Description: Bounding box class definition. * Author: Phil Cheatle * Created: Wed Oct 16 15:18:45 BST 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include "rect.h" // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif /********************************************************************** * TBOX::TBOX() Constructor from 2 ICOORDS * **********************************************************************/ TBOX::TBOX( //constructor const ICOORD pt1, //one corner const ICOORD pt2 //the other corner ) { if (pt1.x () <= pt2.x ()) { if (pt1.y () <= pt2.y ()) { bot_left = pt1; top_right = pt2; } else { bot_left = ICOORD (pt1.x (), pt2.y ()); top_right = ICOORD (pt2.x (), pt1.y ()); } } else { if (pt1.y () <= pt2.y ()) { bot_left = ICOORD (pt2.x (), pt1.y ()); top_right = ICOORD (pt1.x (), pt2.y ()); } else { bot_left = pt2; top_right = pt1; } } } /********************************************************************** * TBOX::TBOX() Constructor from 4 integer values. * Note: It is caller's responsibility to provide values in the right * order. **********************************************************************/ TBOX::TBOX( //constructor inT16 left, inT16 bottom, inT16 right, inT16 top) : bot_left(left, bottom), top_right(right, top) { } // rotate_large constructs the containing bounding box of all 4 // corners after rotating them. It therefore guarantees that all // original content is contained within, but also slightly enlarges the box. void TBOX::rotate_large(const FCOORD& vec) { ICOORD top_left(bot_left.x(), top_right.y()); ICOORD bottom_right(top_right.x(), bot_left.y()); top_left.rotate(vec); bottom_right.rotate(vec); rotate(vec); TBOX box2(top_left, bottom_right); *this += box2; } /********************************************************************** * TBOX::intersection() Build the largest box contained in both boxes * **********************************************************************/ TBOX TBOX::intersection( //shared area box const TBOX &box) const { inT16 left; inT16 bottom; inT16 right; inT16 top; if (overlap (box)) { if (box.bot_left.x () > bot_left.x ()) left = box.bot_left.x (); else left = bot_left.x (); if (box.top_right.x () < top_right.x ()) right = box.top_right.x (); else right = top_right.x (); if (box.bot_left.y () > bot_left.y ()) bottom = box.bot_left.y (); else bottom = bot_left.y (); if (box.top_right.y () < top_right.y ()) top = box.top_right.y (); else top = top_right.y (); } else { left = MAX_INT16; bottom = MAX_INT16; top = -MAX_INT16; right = -MAX_INT16; } return TBOX (left, bottom, right, top); } /********************************************************************** * TBOX::bounding_union() Build the smallest box containing both boxes * **********************************************************************/ TBOX TBOX::bounding_union( //box enclosing both const TBOX &box) const { ICOORD bl; //bottom left ICOORD tr; //top right if (box.bot_left.x () < bot_left.x ()) bl.set_x (box.bot_left.x ()); else bl.set_x (bot_left.x ()); if (box.top_right.x () > top_right.x ()) tr.set_x (box.top_right.x ()); else tr.set_x (top_right.x ()); if (box.bot_left.y () < bot_left.y ()) bl.set_y (box.bot_left.y ()); else bl.set_y (bot_left.y ()); if (box.top_right.y () > top_right.y ()) tr.set_y (box.top_right.y ()); else tr.set_y (top_right.y ()); return TBOX (bl, tr); } /********************************************************************** * TBOX::plot() Paint a box using specified settings * **********************************************************************/ #ifndef GRAPHICS_DISABLED void TBOX::plot( //paint box ScrollView* fd, //where to paint ScrollView::Color fill_colour, //colour for inside ScrollView::Color border_colour //colour for border ) const { fd->Brush(fill_colour); fd->Pen(border_colour); plot(fd); } #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; if (!top_right.Serialize(fp)) return false; return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool TBOX::DeSerialize(bool swap, FILE* fp) { if (!bot_left.DeSerialize(swap, fp)) return false; if (!top_right.DeSerialize(swap, fp)) return false; return true; } /********************************************************************** * operator+= * * Extend one box to include the other (In place union) **********************************************************************/ DLLSYM TBOX & operator+= ( //bounding bounding bx TBOX & op1, //operands const TBOX & op2) { if (op2.bot_left.x () < op1.bot_left.x ()) op1.bot_left.set_x (op2.bot_left.x ()); if (op2.top_right.x () > op1.top_right.x ()) op1.top_right.set_x (op2.top_right.x ()); if (op2.bot_left.y () < op1.bot_left.y ()) op1.bot_left.set_y (op2.bot_left.y ()); if (op2.top_right.y () > op1.top_right.y ()) op1.top_right.set_y (op2.top_right.y ()); return op1; } /********************************************************************** * operator&= * * Reduce one box to intersection with the other (In place intersection) **********************************************************************/ TBOX& operator&=(TBOX& op1, const TBOX& op2) { if (op1.overlap (op2)) { if (op2.bot_left.x () > op1.bot_left.x ()) op1.bot_left.set_x (op2.bot_left.x ()); if (op2.top_right.x () < op1.top_right.x ()) op1.top_right.set_x (op2.top_right.x ()); if (op2.bot_left.y () > op1.bot_left.y ()) op1.bot_left.set_y (op2.bot_left.y ()); if (op2.top_right.y () < op1.top_right.y ()) op1.top_right.set_y (op2.top_right.y ()); } else { op1.bot_left.set_x (MAX_INT16); op1.bot_left.set_y (MAX_INT16); op1.top_right.set_x (-MAX_INT16); op1.top_right.set_y (-MAX_INT16); } return op1; } bool TBOX::x_almost_equal(const TBOX &box, int tolerance) const { return (abs(left() - box.left()) <= tolerance && abs(right() - box.right()) <= tolerance); } bool TBOX::almost_equal(const TBOX &box, int tolerance) const { return (abs(left() - box.left()) <= tolerance && abs(right() - box.right()) <= tolerance && abs(top() - box.top()) <= tolerance && abs(bottom() - box.bottom()) <= tolerance); } tesseract-3.04.01/ccstruct/rect.h000066400000000000000000000375431266071204500166330ustar00rootroot00000000000000/********************************************************************** * File: rect.h (Formerly box.h) * Description: Bounding box class definition. * Author: Phil Cheatle * Created: Wed Oct 16 15:18:45 BST 1991 * * (C) Copyright 1991, 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 RECT_H #define RECT_H #include #include "points.h" #include "ndminx.h" #include "scrollview.h" #include "strngs.h" #include "tprintf.h" class DLLSYM TBOX { // bounding box public: TBOX (): // empty constructor making a null box bot_left (MAX_INT16, MAX_INT16), top_right (-MAX_INT16, -MAX_INT16) { } TBOX( // constructor const ICOORD pt1, // one corner const ICOORD pt2); // the other corner TBOX( // constructor inT16 left, inT16 bottom, inT16 right, inT16 top); TBOX( // box around FCOORD const FCOORD pt); bool null_box() const { // Is box null return ((left () >= right ()) || (top () <= bottom ())); } bool operator==(const TBOX& other) const { return bot_left == other.bot_left && top_right == other.top_right; } inT16 top() const { // coord of top return top_right.y (); } void set_top(int y) { top_right.set_y(y); } inT16 bottom() const { // coord of bottom return bot_left.y (); } void set_bottom(int y) { bot_left.set_y(y); } inT16 left() const { // coord of left return bot_left.x (); } void set_left(int x) { bot_left.set_x(x); } inT16 right() const { // coord of right return top_right.x (); } void set_right(int x) { top_right.set_x(x); } int x_middle() const { return (bot_left.x() + top_right.x()) / 2; } int y_middle() const { return (bot_left.y() + top_right.y()) / 2; } const ICOORD &botleft() const { // access function return bot_left; } ICOORD botright() const { // ~ access function return ICOORD (top_right.x (), bot_left.y ()); } ICOORD topleft() const { // ~ access function return ICOORD (bot_left.x (), top_right.y ()); } const ICOORD &topright() const { // access function return top_right; } inT16 height() const { // how high is it? if (!null_box ()) return top_right.y () - bot_left.y (); else return 0; } inT16 width() const { // how high is it? if (!null_box ()) return top_right.x () - bot_left.x (); else return 0; } inT32 area() const { // what is the area? if (!null_box ()) return width () * height (); else return 0; } // Pads the box on either side by the supplied x,y pad amounts. // NO checks for exceeding any bounds like 0 or an image size. void pad(int xpad, int ypad) { ICOORD pad(xpad, ypad); bot_left -= pad; top_right += pad; } void move_bottom_edge( // move one edge const inT16 y) { // by +/- y bot_left += ICOORD (0, y); } void move_left_edge( // move one edge const inT16 x) { // by +/- x bot_left += ICOORD (x, 0); } void move_right_edge( // move one edge const inT16 x) { // by +/- x top_right += ICOORD (x, 0); } void move_top_edge( // move one edge const inT16 y) { // by +/- y top_right += ICOORD (0, y); } void move( // move box const ICOORD vec) { // by vector bot_left += vec; top_right += vec; } void move( // move box const FCOORD vec) { // by float vector bot_left.set_x ((inT16) floor (bot_left.x () + vec.x ())); // round left bot_left.set_y ((inT16) floor (bot_left.y () + vec.y ())); // round down top_right.set_x ((inT16) ceil (top_right.x () + vec.x ())); // round right top_right.set_y ((inT16) ceil (top_right.y () + vec.y ())); // round up } void scale( // scale box const float f) { // by multiplier bot_left.set_x ((inT16) floor (bot_left.x () * f)); // round left bot_left.set_y ((inT16) floor (bot_left.y () * f)); // round down top_right.set_x ((inT16) ceil (top_right.x () * f)); // round right top_right.set_y ((inT16) ceil (top_right.y () * f)); // round up } void scale( // scale box const FCOORD vec) { // by float vector bot_left.set_x ((inT16) floor (bot_left.x () * vec.x ())); bot_left.set_y ((inT16) floor (bot_left.y () * vec.y ())); top_right.set_x ((inT16) ceil (top_right.x () * vec.x ())); top_right.set_y ((inT16) ceil (top_right.y () * vec.y ())); } // rotate doesn't enlarge the box - it just rotates the bottom-left // and top-right corners. Use rotate_large if you want to guarantee // that all content is contained within the rotated box. void rotate(const FCOORD& vec) { // by vector bot_left.rotate (vec); top_right.rotate (vec); *this = TBOX (bot_left, top_right); } // rotate_large constructs the containing bounding box of all 4 // corners after rotating them. It therefore guarantees that all // original content is contained within, but also slightly enlarges the box. void rotate_large(const FCOORD& vec); bool contains( // is pt inside box const FCOORD pt) const; bool contains( // is box inside box const TBOX &box) const; bool overlap( // do boxes overlap const TBOX &box) const; bool major_overlap( // do boxes overlap more than half const TBOX &box) const; // Do boxes overlap on x axis. bool x_overlap(const TBOX &box) const; // Return the horizontal gap between the boxes. If the boxes // overlap horizontally then the return value is negative, indicating // the amount of the overlap. int x_gap(const TBOX& box) const { return MAX(bot_left.x(), box.bot_left.x()) - MIN(top_right.x(), box.top_right.x()); } // Return the vertical gap between the boxes. If the boxes // overlap vertically then the return value is negative, indicating // the amount of the overlap. int y_gap(const TBOX& box) const { return MAX(bot_left.y(), box.bot_left.y()) - MIN(top_right.y(), box.top_right.y()); } // Do boxes overlap on x axis by more than // half of the width of the narrower box. bool major_x_overlap(const TBOX &box) const; // Do boxes overlap on y axis. bool y_overlap(const TBOX &box) const; // Do boxes overlap on y axis by more than // half of the height of the shorter box. bool major_y_overlap(const TBOX &box) const; // fraction of current box's area covered by other double overlap_fraction(const TBOX &box) const; // fraction of the current box's projected area covered by the other's double x_overlap_fraction(const TBOX& box) const; // fraction of the current box's projected area covered by the other's double y_overlap_fraction(const TBOX& box) const; // Returns true if the boxes are almost equal on x axis. bool x_almost_equal(const TBOX &box, int tolerance) const; // Returns true if the boxes are almost equal bool almost_equal(const TBOX &box, int tolerance) const; TBOX intersection( // shared area box const TBOX &box) const; TBOX bounding_union( // box enclosing both const TBOX &box) const; // Sets the box boundaries to the given coordinates. void set_to_given_coords(int x_min, int y_min, int x_max, int y_max) { bot_left.set_x(x_min); bot_left.set_y(y_min); top_right.set_x(x_max); top_right.set_y(y_max); } void print() const { // print tprintf("Bounding box=(%d,%d)->(%d,%d)\n", left(), bottom(), right(), top()); } // 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 ScrollView* fd) const { // where to paint fd->Rectangle(bot_left.x (), bot_left.y (), top_right.x (), top_right.y ()); } void plot( // paint box ScrollView* fd, // where to paint ScrollView::Color fill_colour, // colour for inside ScrollView::Color border_colour) const; // colour for border #endif // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); friend TBOX& operator+=(TBOX&, const TBOX&); // in place union friend TBOX& operator&=(TBOX&, const TBOX&); // in place intersection private: ICOORD bot_left; // bottom left corner ICOORD top_right; // top right corner }; /********************************************************************** * TBOX::TBOX() Constructor from 1 FCOORD * **********************************************************************/ inline TBOX::TBOX( // constructor const FCOORD pt // floating centre ) { bot_left = ICOORD ((inT16) floor (pt.x ()), (inT16) floor (pt.y ())); top_right = ICOORD ((inT16) ceil (pt.x ()), (inT16) ceil (pt.y ())); } /********************************************************************** * TBOX::contains() Is point within box * **********************************************************************/ inline bool TBOX::contains(const FCOORD pt) const { return ((pt.x () >= bot_left.x ()) && (pt.x () <= top_right.x ()) && (pt.y () >= bot_left.y ()) && (pt.y () <= top_right.y ())); } /********************************************************************** * TBOX::contains() Is box within box * **********************************************************************/ inline bool TBOX::contains(const TBOX &box) const { return (contains (box.bot_left) && contains (box.top_right)); } /********************************************************************** * TBOX::overlap() Do two boxes overlap? * **********************************************************************/ inline bool TBOX::overlap( // do boxes overlap const TBOX &box) const { return ((box.bot_left.x () <= top_right.x ()) && (box.top_right.x () >= bot_left.x ()) && (box.bot_left.y () <= top_right.y ()) && (box.top_right.y () >= bot_left.y ())); } /********************************************************************** * TBOX::major_overlap() Do two boxes overlap by at least half of the smallest? * **********************************************************************/ inline bool TBOX::major_overlap( // Do boxes overlap more that half. const TBOX &box) const { int overlap = MIN(box.top_right.x(), top_right.x()); overlap -= MAX(box.bot_left.x(), bot_left.x()); overlap += overlap; if (overlap < MIN(box.width(), width())) return false; overlap = MIN(box.top_right.y(), top_right.y()); overlap -= MAX(box.bot_left.y(), bot_left.y()); overlap += overlap; if (overlap < MIN(box.height(), height())) return false; return true; } /********************************************************************** * TBOX::overlap_fraction() Fraction of area covered by the other box * **********************************************************************/ inline double TBOX::overlap_fraction(const TBOX &box) const { double fraction = 0.0; if (this->area()) { fraction = this->intersection(box).area() * 1.0 / this->area(); } return fraction; } /********************************************************************** * TBOX::x_overlap() Do two boxes overlap on x-axis * **********************************************************************/ inline bool TBOX::x_overlap(const TBOX &box) const { return ((box.bot_left.x() <= top_right.x()) && (box.top_right.x() >= bot_left.x())); } /********************************************************************** * TBOX::major_x_overlap() Do two boxes overlap by more than half the * width of the narrower box on the x-axis * **********************************************************************/ inline bool TBOX::major_x_overlap(const TBOX &box) const { inT16 overlap = box.width(); if (this->left() > box.left()) { overlap -= this->left() - box.left(); } if (this->right() < box.right()) { overlap -= box.right() - this->right(); } return (overlap >= box.width() / 2 || overlap >= this->width() / 2); } /********************************************************************** * TBOX::y_overlap() Do two boxes overlap on y-axis * **********************************************************************/ inline bool TBOX::y_overlap(const TBOX &box) const { return ((box.bot_left.y() <= top_right.y()) && (box.top_right.y() >= bot_left.y())); } /********************************************************************** * TBOX::major_y_overlap() Do two boxes overlap by more than half the * height of the shorter box on the y-axis * **********************************************************************/ inline bool TBOX::major_y_overlap(const TBOX &box) const { inT16 overlap = box.height(); if (this->bottom() > box.bottom()) { overlap -= this->bottom() - box.bottom(); } if (this->top() < box.top()) { overlap -= box.top() - this->top(); } return (overlap >= box.height() / 2 || overlap >= this->height() / 2); } /********************************************************************** * TBOX::x_overlap_fraction() Calculates the horizontal overlap of the * given boxes as a fraction of this boxes * width. * **********************************************************************/ inline double TBOX::x_overlap_fraction(const TBOX& other) const { int low = MAX(left(), other.left()); int high = MIN(right(), other.right()); int width = right() - left(); if (width == 0) { int x = left(); if (other.left() <= x && x <= other.right()) return 1.0; else return 0.0; } else { return MAX(0, static_cast(high - low) / width); } } /********************************************************************** * TBOX::y_overlap_fraction() Calculates the vertical overlap of the * given boxes as a fraction of this boxes * height. * **********************************************************************/ inline double TBOX::y_overlap_fraction(const TBOX& other) const { int low = MAX(bottom(), other.bottom()); int high = MIN(top(), other.top()); int height = top() - bottom(); if (height == 0) { int y = bottom(); if (other.bottom() <= y && y <= other.top()) return 1.0; else return 0.0; } else { return MAX(0, static_cast(high - low) / height); } } #endif tesseract-3.04.01/ccstruct/rejctmap.cpp000066400000000000000000000273171266071204500200340ustar00rootroot00000000000000/********************************************************************** * File: rejctmap.cpp (Formerly rejmap.c) * Description: REJ and REJMAP class functions. * Author: Phil Cheatle * Created: Thu Jun 9 13:46:38 BST 1994 * * (C) Copyright 1994, 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. * **********************************************************************/ #include "host.h" #include "rejctmap.h" #include "params.h" BOOL8 REJ::perm_rejected() { //Is char perm reject? return (flag (R_TESS_FAILURE) || flag (R_SMALL_XHT) || flag (R_EDGE_CHAR) || flag (R_1IL_CONFLICT) || flag (R_POSTNN_1IL) || flag (R_REJ_CBLOB) || flag (R_BAD_REPETITION) || flag (R_MM_REJECT)); } BOOL8 REJ::rej_before_nn_accept() { return flag (R_POOR_MATCH) || flag (R_NOT_TESS_ACCEPTED) || flag (R_CONTAINS_BLANKS) || flag (R_BAD_PERMUTER); } BOOL8 REJ::rej_between_nn_and_mm() { return flag (R_HYPHEN) || flag (R_DUBIOUS) || flag (R_NO_ALPHANUMS) || flag (R_MOSTLY_REJ) || flag (R_XHT_FIXUP); } BOOL8 REJ::rej_between_mm_and_quality_accept() { return flag (R_BAD_QUALITY); } BOOL8 REJ::rej_between_quality_and_minimal_rej_accept() { return flag (R_DOC_REJ) || flag (R_BLOCK_REJ) || flag (R_ROW_REJ) || flag (R_UNLV_REJ); } BOOL8 REJ::rej_before_mm_accept() { return rej_between_nn_and_mm () || (rej_before_nn_accept () && !flag (R_NN_ACCEPT) && !flag (R_HYPHEN_ACCEPT)); } BOOL8 REJ::rej_before_quality_accept() { return rej_between_mm_and_quality_accept () || (!flag (R_MM_ACCEPT) && rej_before_mm_accept ()); } BOOL8 REJ::rejected() { //Is char rejected? if (flag (R_MINIMAL_REJ_ACCEPT)) return FALSE; else return (perm_rejected () || rej_between_quality_and_minimal_rej_accept () || (!flag (R_QUALITY_ACCEPT) && rej_before_quality_accept ())); } BOOL8 REJ::accept_if_good_quality() { //potential rej? return (rejected () && !perm_rejected () && flag (R_BAD_PERMUTER) && !flag (R_POOR_MATCH) && !flag (R_NOT_TESS_ACCEPTED) && !flag (R_CONTAINS_BLANKS) && (!rej_between_nn_and_mm () && !rej_between_mm_and_quality_accept () && !rej_between_quality_and_minimal_rej_accept ())); } void REJ::setrej_tess_failure() { //Tess generated blank set_flag(R_TESS_FAILURE); } void REJ::setrej_small_xht() { //Small xht char/wd set_flag(R_SMALL_XHT); } void REJ::setrej_edge_char() { //Close to image edge set_flag(R_EDGE_CHAR); } void REJ::setrej_1Il_conflict() { //Initial reject map set_flag(R_1IL_CONFLICT); } void REJ::setrej_postNN_1Il() { //1Il after NN set_flag(R_POSTNN_1IL); } void REJ::setrej_rej_cblob() { //Insert duff blob set_flag(R_REJ_CBLOB); } void REJ::setrej_mm_reject() { //Matrix matcher set_flag(R_MM_REJECT); } void REJ::setrej_bad_repetition() { //Odd repeated char set_flag(R_BAD_REPETITION); } void REJ::setrej_poor_match() { //Failed Rays heuristic set_flag(R_POOR_MATCH); } void REJ::setrej_not_tess_accepted() { //TEMP reject_word set_flag(R_NOT_TESS_ACCEPTED); } void REJ::setrej_contains_blanks() { //TEMP reject_word set_flag(R_CONTAINS_BLANKS); } void REJ::setrej_bad_permuter() { //POTENTIAL reject_word set_flag(R_BAD_PERMUTER); } void REJ::setrej_hyphen() { //PostNN dubious hyphen or . set_flag(R_HYPHEN); } void REJ::setrej_dubious() { //PostNN dubious limit set_flag(R_DUBIOUS); } void REJ::setrej_no_alphanums() { //TEMP reject_word set_flag(R_NO_ALPHANUMS); } void REJ::setrej_mostly_rej() { //TEMP reject_word set_flag(R_MOSTLY_REJ); } void REJ::setrej_xht_fixup() { //xht fixup set_flag(R_XHT_FIXUP); } void REJ::setrej_bad_quality() { //TEMP reject_word set_flag(R_BAD_QUALITY); } void REJ::setrej_doc_rej() { //TEMP reject_word set_flag(R_DOC_REJ); } void REJ::setrej_block_rej() { //TEMP reject_word set_flag(R_BLOCK_REJ); } void REJ::setrej_row_rej() { //TEMP reject_word set_flag(R_ROW_REJ); } void REJ::setrej_unlv_rej() { //TEMP reject_word set_flag(R_UNLV_REJ); } void REJ::setrej_hyphen_accept() { //NN Flipped a char set_flag(R_HYPHEN_ACCEPT); } void REJ::setrej_nn_accept() { //NN Flipped a char set_flag(R_NN_ACCEPT); } void REJ::setrej_mm_accept() { //Matrix matcher set_flag(R_MM_ACCEPT); } void REJ::setrej_quality_accept() { //Quality flip a char set_flag(R_QUALITY_ACCEPT); } void REJ::setrej_minimal_rej_accept() { //Accept all except blank set_flag(R_MINIMAL_REJ_ACCEPT); } void REJ::full_print(FILE *fp) { fprintf (fp, "R_TESS_FAILURE: %s\n", flag (R_TESS_FAILURE) ? "T" : "F"); fprintf (fp, "R_SMALL_XHT: %s\n", flag (R_SMALL_XHT) ? "T" : "F"); fprintf (fp, "R_EDGE_CHAR: %s\n", flag (R_EDGE_CHAR) ? "T" : "F"); fprintf (fp, "R_1IL_CONFLICT: %s\n", flag (R_1IL_CONFLICT) ? "T" : "F"); fprintf (fp, "R_POSTNN_1IL: %s\n", flag (R_POSTNN_1IL) ? "T" : "F"); fprintf (fp, "R_REJ_CBLOB: %s\n", flag (R_REJ_CBLOB) ? "T" : "F"); fprintf (fp, "R_MM_REJECT: %s\n", flag (R_MM_REJECT) ? "T" : "F"); fprintf (fp, "R_BAD_REPETITION: %s\n", flag (R_BAD_REPETITION) ? "T" : "F"); fprintf (fp, "R_POOR_MATCH: %s\n", flag (R_POOR_MATCH) ? "T" : "F"); fprintf (fp, "R_NOT_TESS_ACCEPTED: %s\n", flag (R_NOT_TESS_ACCEPTED) ? "T" : "F"); fprintf (fp, "R_CONTAINS_BLANKS: %s\n", flag (R_CONTAINS_BLANKS) ? "T" : "F"); fprintf (fp, "R_BAD_PERMUTER: %s\n", flag (R_BAD_PERMUTER) ? "T" : "F"); fprintf (fp, "R_HYPHEN: %s\n", flag (R_HYPHEN) ? "T" : "F"); fprintf (fp, "R_DUBIOUS: %s\n", flag (R_DUBIOUS) ? "T" : "F"); fprintf (fp, "R_NO_ALPHANUMS: %s\n", flag (R_NO_ALPHANUMS) ? "T" : "F"); fprintf (fp, "R_MOSTLY_REJ: %s\n", flag (R_MOSTLY_REJ) ? "T" : "F"); fprintf (fp, "R_XHT_FIXUP: %s\n", flag (R_XHT_FIXUP) ? "T" : "F"); fprintf (fp, "R_BAD_QUALITY: %s\n", flag (R_BAD_QUALITY) ? "T" : "F"); fprintf (fp, "R_DOC_REJ: %s\n", flag (R_DOC_REJ) ? "T" : "F"); fprintf (fp, "R_BLOCK_REJ: %s\n", flag (R_BLOCK_REJ) ? "T" : "F"); fprintf (fp, "R_ROW_REJ: %s\n", flag (R_ROW_REJ) ? "T" : "F"); fprintf (fp, "R_UNLV_REJ: %s\n", flag (R_UNLV_REJ) ? "T" : "F"); fprintf (fp, "R_HYPHEN_ACCEPT: %s\n", flag (R_HYPHEN_ACCEPT) ? "T" : "F"); fprintf (fp, "R_NN_ACCEPT: %s\n", flag (R_NN_ACCEPT) ? "T" : "F"); fprintf (fp, "R_MM_ACCEPT: %s\n", flag (R_MM_ACCEPT) ? "T" : "F"); fprintf (fp, "R_QUALITY_ACCEPT: %s\n", flag (R_QUALITY_ACCEPT) ? "T" : "F"); fprintf (fp, "R_MINIMAL_REJ_ACCEPT: %s\n", flag (R_MINIMAL_REJ_ACCEPT) ? "T" : "F"); } //The REJMAP class has been hacked to use alloc_struct instead of new []. //This is to reduce memory fragmentation only as it is rather kludgy. //alloc_struct by-passes the call to the contsructor of REJ on each //array element. Although the constructor is empty, the BITS16 members //do have a constructor which sets all the flags to 0. The memset //replaces this functionality. REJMAP::REJMAP( //classwise copy const REJMAP &source) { REJ *to; REJ *from = source.ptr; int i; len = source.length (); if (len > 0) { ptr = (REJ *) alloc_struct (len * sizeof (REJ), "REJ"); to = ptr; for (i = 0; i < len; i++) { *to = *from; to++; from++; } } else ptr = NULL; } REJMAP & REJMAP::operator= ( //assign REJMAP const REJMAP & source //from this ) { REJ * to; REJ * from = source.ptr; int i; initialise (source.len); to = ptr; for (i = 0; i < len; i++) { *to = *from; to++; from++; } return *this; } void REJMAP::initialise( //Redefine map inT16 length) { if (ptr != NULL) free_struct (ptr, len * sizeof (REJ), "REJ"); len = length; if (len > 0) ptr = (REJ *) memset (alloc_struct (len * sizeof (REJ), "REJ"), 0, len * sizeof (REJ)); else ptr = NULL; } inT16 REJMAP::accept_count() { //How many accepted? int i; inT16 count = 0; for (i = 0; i < len; i++) { if (ptr[i].accepted ()) count++; } return count; } BOOL8 REJMAP::recoverable_rejects() { //Any non perm rejs? int i; for (i = 0; i < len; i++) { if (ptr[i].recoverable ()) return TRUE; } return FALSE; } BOOL8 REJMAP::quality_recoverable_rejects() { //Any potential rejs? int i; for (i = 0; i < len; i++) { if (ptr[i].accept_if_good_quality ()) return TRUE; } return FALSE; } void REJMAP::remove_pos( //Cut out an element inT16 pos //element to remove ) { REJ *new_ptr; //new, smaller map int i; ASSERT_HOST (pos >= 0); ASSERT_HOST (pos < len); ASSERT_HOST (len > 0); len--; if (len > 0) new_ptr = (REJ *) memset (alloc_struct (len * sizeof (REJ), "REJ"), 0, len * sizeof (REJ)); else new_ptr = NULL; for (i = 0; i < pos; i++) new_ptr[i] = ptr[i]; //copy pre pos for (; pos < len; pos++) new_ptr[pos] = ptr[pos + 1]; //copy post pos //delete old map free_struct (ptr, (len + 1) * sizeof (REJ), "REJ"); ptr = new_ptr; } void REJMAP::print(FILE *fp) { int i; char buff[512]; for (i = 0; i < len; i++) { buff[i] = ptr[i].display_char (); } buff[i] = '\0'; fprintf (fp, "\"%s\"", buff); } void REJMAP::full_print(FILE *fp) { int i; for (i = 0; i < len; i++) { ptr[i].full_print (fp); fprintf (fp, "\n"); } } void REJMAP::rej_word_small_xht() { //Reject whole word int i; for (i = 0; i < len; i++) { ptr[i].setrej_small_xht (); } } void REJMAP::rej_word_tess_failure() { //Reject whole word int i; for (i = 0; i < len; i++) { ptr[i].setrej_tess_failure (); } } void REJMAP::rej_word_not_tess_accepted() { //Reject whole word int i; for (i = 0; i < len; i++) { if (ptr[i].accepted()) ptr[i].setrej_not_tess_accepted(); } } void REJMAP::rej_word_contains_blanks() { //Reject whole word int i; for (i = 0; i < len; i++) { if (ptr[i].accepted()) ptr[i].setrej_contains_blanks(); } } void REJMAP::rej_word_bad_permuter() { //Reject whole word int i; for (i = 0; i < len; i++) { if (ptr[i].accepted()) ptr[i].setrej_bad_permuter (); } } void REJMAP::rej_word_xht_fixup() { //Reject whole word int i; for (i = 0; i < len; i++) { if (ptr[i].accepted()) ptr[i].setrej_xht_fixup(); } } void REJMAP::rej_word_no_alphanums() { //Reject whole word int i; for (i = 0; i < len; i++) { if (ptr[i].accepted()) ptr[i].setrej_no_alphanums(); } } void REJMAP::rej_word_mostly_rej() { //Reject whole word int i; for (i = 0; i < len; i++) { if (ptr[i].accepted()) ptr[i].setrej_mostly_rej(); } } void REJMAP::rej_word_bad_quality() { //Reject whole word int i; for (i = 0; i < len; i++) { if (ptr[i].accepted()) ptr[i].setrej_bad_quality(); } } void REJMAP::rej_word_doc_rej() { //Reject whole word int i; for (i = 0; i < len; i++) { if (ptr[i].accepted()) ptr[i].setrej_doc_rej(); } } void REJMAP::rej_word_block_rej() { //Reject whole word int i; for (i = 0; i < len; i++) { if (ptr[i].accepted()) ptr[i].setrej_block_rej(); } } void REJMAP::rej_word_row_rej() { //Reject whole word int i; for (i = 0; i < len; i++) { if (ptr[i].accepted()) ptr[i].setrej_row_rej(); } } tesseract-3.04.01/ccstruct/rejctmap.h000066400000000000000000000226711266071204500174770ustar00rootroot00000000000000/********************************************************************** * File: rejctmap.h (Formerly rejmap.h) * Description: REJ and REJMAP class functions. * Author: Phil Cheatle * Created: Thu Jun 9 13:46:38 BST 1994 * * (C) Copyright 1994, 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. * This module may look unnecessarily verbose, but here's the philosophy... ALL processing of the reject map is done in this module. There are lots of separate calls to set reject/accept flags. These have DELIBERATELY been kept distinct so that this module can decide what to do. Basically, there is a flag for each sort of rejection or acceptance. This provides a history of what has happened to EACH character. Determining whether a character is CURRENTLY rejected depends on implicit understanding of the SEQUENCE of possible calls. The flags are defined and grouped in the REJ_FLAGS enum. These groupings are used in determining a characters CURRENT rejection status. Basically, a character is ACCEPTED if none of the permanent rej flags are set AND ( the character has never been rejected OR an accept flag is set which is LATER than the latest reject flag ) IT IS FUNDAMENTAL THAT ANYONE HACKING THIS CODE UNDERSTANDS THE SIGNIFICANCE OF THIS IMPLIED TEMPORAL ORDERING OF THE FLAGS!!!! **********************************************************************/ #ifndef REJCTMAP_H #define REJCTMAP_H #ifdef __UNIX__ #include #endif #include "memry.h" #include "bits16.h" #include "params.h" enum REJ_FLAGS { /* Reject modes which are NEVER overridden */ R_TESS_FAILURE, // PERM Tess didn't classify R_SMALL_XHT, // PERM Xht too small R_EDGE_CHAR, // PERM Too close to edge of image R_1IL_CONFLICT, // PERM 1Il confusion R_POSTNN_1IL, // PERM 1Il unrejected by NN R_REJ_CBLOB, // PERM Odd blob R_MM_REJECT, // PERM Matrix match rejection (m's) R_BAD_REPETITION, // TEMP Repeated char which doesn't match trend /* Initial reject modes (pre NN_ACCEPT) */ R_POOR_MATCH, // TEMP Ray's original heuristic (Not used) R_NOT_TESS_ACCEPTED, // TEMP Tess didn't accept WERD R_CONTAINS_BLANKS, // TEMP Tess failed on other chs in WERD R_BAD_PERMUTER, // POTENTIAL Bad permuter for WERD /* Reject modes generated after NN_ACCEPT but before MM_ACCEPT */ R_HYPHEN, // TEMP Post NN dodgy hyphen or full stop R_DUBIOUS, // TEMP Post NN dodgy chars R_NO_ALPHANUMS, // TEMP No alphanumerics in word after NN R_MOSTLY_REJ, // TEMP Most of word rejected so rej the rest R_XHT_FIXUP, // TEMP Xht tests unsure /* Reject modes generated after MM_ACCEPT but before QUALITY_ACCEPT */ R_BAD_QUALITY, // TEMP Quality metrics bad for WERD /* Reject modes generated after QUALITY_ACCEPT but before MINIMAL_REJ accep*/ R_DOC_REJ, // TEMP Document rejection R_BLOCK_REJ, // TEMP Block rejection R_ROW_REJ, // TEMP Row rejection R_UNLV_REJ, // TEMP ~ turned to - or ^ turned to space /* Accept modes which occur between the above rejection groups */ R_NN_ACCEPT, //NN acceptance R_HYPHEN_ACCEPT, //Hyphen acceptance R_MM_ACCEPT, //Matrix match acceptance R_QUALITY_ACCEPT, //Accept word in good quality doc R_MINIMAL_REJ_ACCEPT //Accept EVERYTHING except tess failures }; /* REJECT MAP VALUES */ #define MAP_ACCEPT '1' #define MAP_REJECT_PERM '0' #define MAP_REJECT_TEMP '2' #define MAP_REJECT_POTENTIAL '3' class REJ { BITS16 flags1; BITS16 flags2; void set_flag(REJ_FLAGS rej_flag) { if (rej_flag < 16) flags1.turn_on_bit (rej_flag); else flags2.turn_on_bit (rej_flag - 16); } BOOL8 rej_before_nn_accept(); BOOL8 rej_between_nn_and_mm(); BOOL8 rej_between_mm_and_quality_accept(); BOOL8 rej_between_quality_and_minimal_rej_accept(); BOOL8 rej_before_mm_accept(); BOOL8 rej_before_quality_accept(); public: REJ() { //constructor } REJ( //classwise copy const REJ &source) { flags1 = source.flags1; flags2 = source.flags2; } REJ & operator= ( //assign REJ const REJ & source) { //from this flags1 = source.flags1; flags2 = source.flags2; return *this; } BOOL8 flag(REJ_FLAGS rej_flag) { if (rej_flag < 16) return flags1.bit (rej_flag); else return flags2.bit (rej_flag - 16); } char display_char() { if (perm_rejected ()) return MAP_REJECT_PERM; else if (accept_if_good_quality ()) return MAP_REJECT_POTENTIAL; else if (rejected ()) return MAP_REJECT_TEMP; else return MAP_ACCEPT; } BOOL8 perm_rejected(); //Is char perm reject? BOOL8 rejected(); //Is char rejected? BOOL8 accepted() { //Is char accepted? return !rejected (); } //potential rej? BOOL8 accept_if_good_quality(); BOOL8 recoverable() { return (rejected () && !perm_rejected ()); } void setrej_tess_failure(); //Tess generated blank void setrej_small_xht(); //Small xht char/wd void setrej_edge_char(); //Close to image edge void setrej_1Il_conflict(); //Initial reject map void setrej_postNN_1Il(); //1Il after NN void setrej_rej_cblob(); //Insert duff blob void setrej_mm_reject(); //Matrix matcher //Odd repeated char void setrej_bad_repetition(); void setrej_poor_match(); //Failed Rays heuristic //TEMP reject_word void setrej_not_tess_accepted(); //TEMP reject_word void setrej_contains_blanks(); void setrej_bad_permuter(); //POTENTIAL reject_word void setrej_hyphen(); //PostNN dubious hyph or . void setrej_dubious(); //PostNN dubious limit void setrej_no_alphanums(); //TEMP reject_word void setrej_mostly_rej(); //TEMP reject_word void setrej_xht_fixup(); //xht fixup void setrej_bad_quality(); //TEMP reject_word void setrej_doc_rej(); //TEMP reject_word void setrej_block_rej(); //TEMP reject_word void setrej_row_rej(); //TEMP reject_word void setrej_unlv_rej(); //TEMP reject_word void setrej_nn_accept(); //NN Flipped a char void setrej_hyphen_accept(); //Good aspect ratio void setrej_mm_accept(); //Matrix matcher //Quality flip a char void setrej_quality_accept(); //Accept all except blank void setrej_minimal_rej_accept(); void full_print(FILE *fp); }; class REJMAP { REJ *ptr; //ptr to the chars inT16 len; //Number of chars public: REJMAP() { //constructor ptr = NULL; len = 0; } REJMAP( //classwise copy const REJMAP &rejmap); REJMAP & operator= ( //assign REJMAP const REJMAP & source); //from this ~REJMAP () { //destructor if (ptr != NULL) free_struct (ptr, len * sizeof (REJ), "REJ"); } void initialise( //Redefine map inT16 length); REJ & operator[]( //access function inT16 index) const //map index { ASSERT_HOST (index < len); return ptr[index]; //no bounds checks } inT32 length() const { //map length return len; } inT16 accept_count(); //How many accepted? inT16 reject_count() { //How many rejects? return len - accept_count (); } void remove_pos( //Cut out an element inT16 pos); //element to remove void print(FILE *fp); void full_print(FILE *fp); BOOL8 recoverable_rejects(); //Any non perm rejs? BOOL8 quality_recoverable_rejects(); //Any potential rejs? void rej_word_small_xht(); //Reject whole word //Reject whole word void rej_word_tess_failure(); void rej_word_not_tess_accepted(); //Reject whole word //Reject whole word void rej_word_contains_blanks(); //Reject whole word void rej_word_bad_permuter(); void rej_word_xht_fixup(); //Reject whole word //Reject whole word void rej_word_no_alphanums(); void rej_word_mostly_rej(); //Reject whole word void rej_word_bad_quality(); //Reject whole word void rej_word_doc_rej(); //Reject whole word void rej_word_block_rej(); //Reject whole word void rej_word_row_rej(); //Reject whole word }; #endif tesseract-3.04.01/ccstruct/seam.cpp000066400000000000000000000227031266071204500171460ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: seam.c (Formerly seam.c) * Description: * Author: Mark Seaman, OCR Technology * Created: Fri Oct 16 14:37:00 1987 * Modified: Fri May 17 16:30:13 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 "seam.h" #include "blobs.h" #include "tprintf.h" /*---------------------------------------------------------------------- Public Function Code ----------------------------------------------------------------------*/ // Returns the bounding box of all the points in the seam. TBOX SEAM::bounding_box() const { TBOX box(location_.x, location_.y, location_.x, location_.y); for (int s = 0; s < num_splits_; ++s) { box += splits_[s].bounding_box(); } return box; } // Returns true if other can be combined into *this. bool SEAM::CombineableWith(const SEAM& other, int max_x_dist, float max_total_priority) const { int dist = location_.x - other.location_.x; if (-max_x_dist < dist && dist < max_x_dist && num_splits_ + other.num_splits_ <= kMaxNumSplits && priority_ + other.priority_ < max_total_priority && !OverlappingSplits(other) && !SharesPosition(other)) { return true; } else { return false; } } // Combines other into *this. Only works if CombinableWith returned true. void SEAM::CombineWith(const SEAM& other) { priority_ += other.priority_; location_ += other.location_; location_ /= 2; for (int s = 0; s < other.num_splits_ && num_splits_ < kMaxNumSplits; ++s) splits_[num_splits_++] = other.splits_[s]; } // Returns true if the splits in *this SEAM appear OK in the sense that they // do not cross any outlines and do not chop off any ridiculously small // pieces. bool SEAM::IsHealthy(const TBLOB& blob, int min_points, int min_area) const { // TODO(rays) Try testing all the splits. Duplicating original code for now, // which tested only the first. return num_splits_ == 0 || splits_[0].IsHealthy(blob, min_points, min_area); } // Computes the widthp_/widthn_ range for all existing SEAMs and for *this // seam, which is about to be inserted at insert_index. Returns false if // any of the computations fails, as this indicates an invalid chop. // widthn_/widthp_ are only changed if modify is true. bool SEAM::PrepareToInsertSeam(const GenericVector& seams, const GenericVector& blobs, int insert_index, bool modify) { for (int s = 0; s < insert_index; ++s) { if (!seams[s]->FindBlobWidth(blobs, s, modify)) return false; } if (!FindBlobWidth(blobs, insert_index, modify)) return false; for (int s = insert_index; s < seams.size(); ++s) { if (!seams[s]->FindBlobWidth(blobs, s + 1, modify)) return false; } return true; } // Computes the widthp_/widthn_ range. Returns false if not all the splits // are accounted for. widthn_/widthp_ are only changed if modify is true. bool SEAM::FindBlobWidth(const GenericVector& blobs, int index, bool modify) { int num_found = 0; if (modify) { widthp_ = 0; widthn_ = 0; } for (int s = 0; s < num_splits_; ++s) { const SPLIT& split = splits_[s]; bool found_split = split.ContainedByBlob(*blobs[index]); // Look right. for (int b = index + 1; !found_split && b < blobs.size(); ++b) { found_split = split.ContainedByBlob(*blobs[b]); if (found_split && b - index > widthp_ && modify) widthp_ = b - index; } // Look left. for (int b = index - 1; !found_split && b >= 0; --b) { found_split = split.ContainedByBlob(*blobs[b]); if (found_split && index - b > widthn_ && modify) widthn_ = index - b; } if (found_split) ++num_found; } return num_found == num_splits_; } // Splits this blob into two blobs by applying the splits included in // *this SEAM void SEAM::ApplySeam(bool italic_blob, TBLOB* blob, TBLOB* other_blob) const { for (int s = 0; s < num_splits_; ++s) { splits_[s].SplitOutlineList(blob->outlines); } blob->ComputeBoundingBoxes(); divide_blobs(blob, other_blob, italic_blob, location_); blob->EliminateDuplicateOutlines(); other_blob->EliminateDuplicateOutlines(); blob->CorrectBlobOrder(other_blob); } // Undoes ApplySeam by removing the seam between these two blobs. // Produces one blob as a result, and deletes other_blob. void SEAM::UndoSeam(TBLOB* blob, TBLOB* other_blob) const { if (blob->outlines == NULL) { blob->outlines = other_blob->outlines; other_blob->outlines = NULL; } TESSLINE* outline = blob->outlines; while (outline->next) outline = outline->next; outline->next = other_blob->outlines; other_blob->outlines = NULL; delete other_blob; for (int s = 0; s < num_splits_; ++s) { splits_[s].UnsplitOutlineList(blob); } blob->ComputeBoundingBoxes(); blob->EliminateDuplicateOutlines(); } // Prints everything in *this SEAM. void SEAM::Print(const char* label) const { tprintf(label); tprintf(" %6.2f @ (%d,%d), p=%d, n=%d ", priority_, location_.x, location_.y, widthp_, widthn_); for (int s = 0; s < num_splits_; ++s) { splits_[s].Print(); if (s + 1 < num_splits_) tprintf(", "); } tprintf("\n"); } // Prints a collection of SEAMs. /* static */ void SEAM::PrintSeams(const char* label, const GenericVector& seams) { if (!seams.empty()) { tprintf("%s\n", label); for (int x = 0; x < seams.size(); ++x) { tprintf("%2d: ", x); seams[x]->Print(""); } tprintf("\n"); } } #ifndef GRAPHICS_DISABLED // Draws the seam in the given window. void SEAM::Mark(ScrollView* window) const { for (int s = 0; s < num_splits_; ++s) splits_[s].Mark(window); } #endif // Break up the blobs in this chain so that they are all independent. // This operation should undo the affect of join_pieces. /* static */ void SEAM::BreakPieces(const GenericVector& seams, const GenericVector& blobs, int first, int last) { for (int x = first; x < last; ++x) seams[x]->Reveal(); TESSLINE* outline = blobs[first]->outlines; int next_blob = first + 1; while (outline != NULL && next_blob <= last) { if (outline->next == blobs[next_blob]->outlines) { outline->next = NULL; outline = blobs[next_blob]->outlines; ++next_blob; } else { outline = outline->next; } } } // Join a group of base level pieces into a single blob that can then // be classified. /* static */ void SEAM::JoinPieces(const GenericVector& seams, const GenericVector& blobs, int first, int last) { TESSLINE* outline = blobs[first]->outlines; if (!outline) return; for (int x = first; x < last; ++x) { SEAM *seam = seams[x]; if (x - seam->widthn_ >= first && x + seam->widthp_ < last) seam->Hide(); while (outline->next) outline = outline->next; outline->next = blobs[x + 1]->outlines; } } // Hides the seam so the outlines appear not to be cut by it. void SEAM::Hide() const { for (int s = 0; s < num_splits_; ++s) { splits_[s].Hide(); } } // Undoes hide, so the outlines are cut by the seam. void SEAM::Reveal() const { for (int s = 0; s < num_splits_; ++s) { splits_[s].Reveal(); } } // Computes and returns, but does not set, the full priority of *this SEAM. float SEAM::FullPriority(int xmin, int xmax, double overlap_knob, int centered_maxwidth, double center_knob, double width_change_knob) const { if (num_splits_ == 0) return 0.0f; for (int s = 1; s < num_splits_; ++s) { splits_[s].SplitOutline(); } float full_priority = priority_ + splits_[0].FullPriority(xmin, xmax, overlap_knob, centered_maxwidth, center_knob, width_change_knob); for (int s = num_splits_ - 1; s >= 1; --s) { splits_[s].UnsplitOutlines(); } return full_priority; } /** * @name start_seam_list * * Initialize a list of seams that match the original number of blobs * present in the starting segmentation. Each of the seams created * by this routine have location information only. */ void start_seam_list(TWERD* word, GenericVector* seam_array) { seam_array->truncate(0); TPOINT location; 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_array->push_back(new SEAM(0.0f, location)); } } tesseract-3.04.01/ccstruct/seam.h000066400000000000000000000174021266071204500166130ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: seam.h (Formerly seam.h) * Description: * Author: Mark Seaman, SW Productivity * Created: Fri Oct 16 14:37:00 1987 * Modified: Thu May 16 17:05:52 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 SEAM_H #define SEAM_H // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif /*---------------------------------------------------------------------- I n c l u d e s ----------------------------------------------------------------------*/ #include "blobs.h" #include "split.h" /*---------------------------------------------------------------------- T y p e s ----------------------------------------------------------------------*/ typedef float PRIORITY; /* PRIORITY */ class SEAM { public: // A seam with no splits SEAM(float priority, const TPOINT& location) : priority_(priority), location_(location), widthp_(0), widthn_(0), num_splits_(0) {} // A seam with a single split point. SEAM(float priority, const TPOINT& location, const SPLIT& split) : priority_(priority), location_(location), widthp_(0), widthn_(0), num_splits_(1) { splits_[0] = split; } // Default copy constructor, operator= and destructor are OK! // Accessors. float priority() const { return priority_; } void set_priority(float priority) { priority_ = priority; } bool HasAnySplits() const { return num_splits_ > 0; } // Returns the bounding box of all the points in the seam. TBOX bounding_box() const; // Returns true if other can be combined into *this. bool CombineableWith(const SEAM& other, int max_x_dist, float max_total_priority) const; // Combines other into *this. Only works if CombinableWith returned true. void CombineWith(const SEAM& other); // Returns true if the given blob contains all splits of *this SEAM. bool ContainedByBlob(const TBLOB& blob) const { for (int s = 0; s < num_splits_; ++s) { if (!splits_[s].ContainedByBlob(blob)) return false; } return true; } // Returns true if the given EDGEPT is used by this SEAM, checking only // the EDGEPT pointer, not the coordinates. bool UsesPoint(const EDGEPT* point) const { for (int s = 0; s < num_splits_; ++s) { if (splits_[s].UsesPoint(point)) return true; } return false; } // Returns true if *this and other share any common point, by coordinates. bool SharesPosition(const SEAM& other) const { for (int s = 0; s < num_splits_; ++s) { for (int t = 0; t < other.num_splits_; ++t) if (splits_[s].SharesPosition(other.splits_[t])) return true; } return false; } // Returns true if *this and other have any vertically overlapping splits. bool OverlappingSplits(const SEAM& other) const { for (int s = 0; s < num_splits_; ++s) { TBOX split1_box = splits_[s].bounding_box(); for (int t = 0; t < other.num_splits_; ++t) { TBOX split2_box = other.splits_[t].bounding_box(); if (split1_box.y_overlap(split2_box)) return true; } } return false; } // Marks the edgepts used by the seam so the segments made by the cut // never get split further by another seam in the future. void Finalize() { for (int s = 0; s < num_splits_; ++s) { splits_[s].point1->MarkChop(); splits_[s].point2->MarkChop(); } } // Returns true if the splits in *this SEAM appear OK in the sense that they // do not cross any outlines and do not chop off any ridiculously small // pieces. bool IsHealthy(const TBLOB& blob, int min_points, int min_area) const; // Computes the widthp_/widthn_ range for all existing SEAMs and for *this // seam, which is about to be inserted at insert_index. Returns false if // any of the computations fails, as this indicates an invalid chop. // widthn_/widthp_ are only changed if modify is true. bool PrepareToInsertSeam(const GenericVector& seams, const GenericVector& blobs, int insert_index, bool modify); // Computes the widthp_/widthn_ range. Returns false if not all the splits // are accounted for. widthn_/widthp_ are only changed if modify is true. bool FindBlobWidth(const GenericVector& blobs, int index, bool modify); // Splits this blob into two blobs by applying the splits included in // *this SEAM void ApplySeam(bool italic_blob, TBLOB* blob, TBLOB* other_blob) const; // Undoes ApplySeam by removing the seam between these two blobs. // Produces one blob as a result, and deletes other_blob. void UndoSeam(TBLOB* blob, TBLOB* other_blob) const; // Prints everything in *this SEAM. void Print(const char* label) const; // Prints a collection of SEAMs. static void PrintSeams(const char* label, const GenericVector& seams); #ifndef GRAPHICS_DISABLED // Draws the seam in the given window. void Mark(ScrollView* window) const; #endif // Break up the blobs in this chain so that they are all independent. // This operation should undo the affect of join_pieces. static void BreakPieces(const GenericVector& seams, const GenericVector& blobs, int first, int last); // Join a group of base level pieces into a single blob that can then // be classified. static void JoinPieces(const GenericVector& seams, const GenericVector& blobs, int first, int last); // Hides the seam so the outlines appear not to be cut by it. void Hide() const; // Undoes hide, so the outlines are cut by the seam. void Reveal() const; // Computes and returns, but does not set, the full priority of *this SEAM. // The arguments here are config parameters defined in Wordrec. Add chop_ // to the beginning of the name. float FullPriority(int xmin, int xmax, double overlap_knob, int centered_maxwidth, double center_knob, double width_change_knob) const; private: // Maximum number of splits that a SEAM can hold. static const int kMaxNumSplits = 3; // Priority of this split. Lower is better. float priority_; // Position of the middle of the seam. TPOINT location_; // A range such that all splits in *this SEAM are contained within blobs in // the range [index - widthn_,index + widthp_] where index is the index of // this SEAM in the seams vector. inT8 widthp_; inT8 widthn_; // Number of splits_ that are used. inT8 num_splits_; // Set of pairs of points that are the ends of each split in the SEAM. SPLIT splits_[kMaxNumSplits]; }; /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ void start_seam_list(TWERD* word, GenericVector* seam_array); #endif tesseract-3.04.01/ccstruct/split.cpp000066400000000000000000000267341266071204500173640ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: split.c (Formerly split.c) * Description: * Author: Mark Seaman, OCR Technology * Created: Fri Oct 16 14:37:00 1987 * Modified: Fri May 17 16:27:49 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 automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "split.h" #include "coutln.h" #include "tprintf.h" #ifdef __UNIX__ #include #endif /*---------------------------------------------------------------------- V a r i a b l e s ----------------------------------------------------------------------*/ // Limit on the amount of penalty for the chop being off-center. const int kCenterGradeCap = 25; // Ridiculously large priority for splits that are no use. const double kBadPriority = 999.0; BOOL_VAR(wordrec_display_splits, 0, "Display splits"); // Returns the bounding box of all the points in the split. TBOX SPLIT::bounding_box() const { return TBOX( MIN(point1->pos.x, point2->pos.x), MIN(point1->pos.y, point2->pos.y), MAX(point1->pos.x, point2->pos.x), MAX(point1->pos.y, point2->pos.y)); } // Hides the SPLIT so the outlines appear not to be cut by it. void SPLIT::Hide() const { EDGEPT* edgept = point1; do { edgept->Hide(); edgept = edgept->next; } while (!edgept->EqualPos(*point2) && edgept != point1); edgept = point2; do { edgept->Hide(); edgept = edgept->next; } while (!edgept->EqualPos(*point1) && edgept != point2); } // Undoes hide, so the outlines are cut by the SPLIT. void SPLIT::Reveal() const { EDGEPT* edgept = point1; do { edgept->Reveal(); edgept = edgept->next; } while (!edgept->EqualPos(*point2) && edgept != point1); edgept = point2; do { edgept->Reveal(); edgept = edgept->next; } while (!edgept->EqualPos(*point1) && edgept != point2); } // Compute a split priority based on the bounding boxes of the parts. // The arguments here are config parameters defined in Wordrec. Add chop_ // to the beginning of the name. float SPLIT::FullPriority(int xmin, int xmax, double overlap_knob, int centered_maxwidth, double center_knob, double width_change_knob) const { TBOX box1 = Box12(); TBOX box2 = Box21(); int min_left = MIN(box1.left(), box2.left()); int max_right = MAX(box1.right(), box2.right()); if (xmin < min_left && xmax > max_right) return kBadPriority; float grade = 0.0f; // grade_overlap. int width1 = box1.width(); int width2 = box2.width(); int min_width = MIN(width1, width2); int overlap = -box1.x_gap(box2); if (overlap == min_width) { grade += 100.0f; // Total overlap. } else { if (2 * overlap > min_width) overlap += 2 * overlap - min_width; if (overlap > 0) grade += overlap_knob * overlap; } // grade_center_of_blob. if (width1 <= centered_maxwidth || width2 <= centered_maxwidth) { grade += MIN(kCenterGradeCap, center_knob * abs(width1 - width2)); } // grade_width_change. float width_change_grade = 20 - (max_right - min_left - MAX(width1, width2)); if (width_change_grade > 0.0f) grade += width_change_grade * width_change_knob; return grade; } // Returns true if *this SPLIT appears OK in the sense that it does not cross // any outlines and does not chop off any ridiculously small pieces. bool SPLIT::IsHealthy(const TBLOB& blob, int min_points, int min_area) const { return !IsLittleChunk(min_points, min_area) && !blob.SegmentCrossesOutline(point1->pos, point2->pos); } // Returns true if the split generates a small chunk in terms of either area // or number of points. bool SPLIT::IsLittleChunk(int min_points, int min_area) const { if (point1->ShortNonCircularSegment(min_points, point2) && point1->SegmentArea(point2) < min_area) { return true; } if (point2->ShortNonCircularSegment(min_points, point1) && point2->SegmentArea(point1) < min_area) { return true; } return false; } /********************************************************************** * make_edgept * * Create an EDGEPT and hook it into an existing list of edge points. **********************************************************************/ EDGEPT *make_edgept(int x, int y, EDGEPT *next, EDGEPT *prev) { EDGEPT *this_edgept; /* Create point */ 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; prev->next = this_edgept; next->prev = this_edgept; /* Set up vec entries */ this_edgept->vec.x = this_edgept->next->pos.x - x; 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; } /********************************************************************** * remove_edgept * * Remove a given EDGEPT from its list and delete it. **********************************************************************/ 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; prev->vec.y = next->pos.y - prev->pos.y; delete point; } /********************************************************************** * Print * * Shows the coordinates of both points in a split. **********************************************************************/ void SPLIT::Print() const { tprintf("(%d,%d)--(%d,%d)", point1->pos.x, point1->pos.y, point2->pos.x, point2->pos.y); } #ifndef GRAPHICS_DISABLED // Draws the split in the given window. void SPLIT::Mark(ScrollView* window) const { window->Pen(ScrollView::GREEN); window->Line(point1->pos.x, point1->pos.y, point2->pos.x, point2->pos.y); window->UpdateWindow(); } #endif // Creates two outlines out of one by splitting the original one in half. // Inserts the resulting outlines into the given list. void SPLIT::SplitOutlineList(TESSLINE* outlines) const { SplitOutline(); while (outlines->next != NULL) outlines = outlines->next; outlines->next = new TESSLINE; outlines->next->loop = point1; outlines->next->ComputeBoundingBox(); outlines = outlines->next; outlines->next = new TESSLINE; outlines->next->loop = point2; outlines->next->ComputeBoundingBox(); outlines->next->next = NULL; } // Makes a split between these two edge points, but does not affect the // outlines to which they belong. void SPLIT::SplitOutline() const { EDGEPT* temp2 = point2->next; EDGEPT* temp1 = point1->next; /* Create two new points */ EDGEPT* new_point1 = make_edgept(point1->pos.x, point1->pos.y, temp1, point2); EDGEPT* new_point2 = make_edgept(point2->pos.x, point2->pos.y, temp2, point1); // 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 = point1->src_outline; new_point1->start_step = point1->start_step; new_point1->step_count = point1->step_count; new_point2->src_outline = point2->src_outline; new_point2->start_step = point2->start_step; new_point2->step_count = point2->step_count; point1->src_outline = NULL; point1->start_step = 0; point1->step_count = 0; point2->src_outline = NULL; point2->start_step = 0; point2->step_count = 0; } // Undoes the effect of SplitOutlineList, correcting the outlines for undoing // the split, but possibly leaving some duplicate outlines. void SPLIT::UnsplitOutlineList(TBLOB* blob) const { /* Modify edge points */ UnsplitOutlines(); TESSLINE* outline1 = new TESSLINE; outline1->next = blob->outlines; blob->outlines = outline1; outline1->loop = point1; TESSLINE* outline2 = new TESSLINE; outline2->next = blob->outlines; blob->outlines = outline2; outline2->loop = point2; } // Removes the split that was put between these two points. void SPLIT::UnsplitOutlines() const { EDGEPT* tmp1 = point1->next; EDGEPT* tmp2 = point2->next; tmp1->next->prev = point2; tmp2->next->prev = point1; // tmp2 is coincident with point1. point1 takes tmp2's place as tmp2 is // deleted. point1->next = tmp2->next; point1->src_outline = tmp2->src_outline; point1->start_step = tmp2->start_step; point1->step_count = tmp2->step_count; // Likewise point2 takes tmp1's place. point2->next = tmp1->next; point2->src_outline = tmp1->src_outline; point2->start_step = tmp1->start_step; point2->step_count = tmp1->step_count; delete tmp1; delete tmp2; point1->vec.x = point1->next->pos.x - point1->pos.x; point1->vec.y = point1->next->pos.y - point1->pos.y; point2->vec.x = point2->next->pos.x - point2->pos.x; point2->vec.y = point2->next->pos.y - point2->pos.y; } tesseract-3.04.01/ccstruct/split.h000066400000000000000000000120321266071204500170130ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: split.h (Formerly split.h) * Description: * Author: Mark Seaman, SW Productivity * Created: Fri Oct 16 14:37:00 1987 * Modified: Mon May 13 10:49:23 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 SPLIT_H #define SPLIT_H /*---------------------------------------------------------------------- I n c l u d e s ----------------------------------------------------------------------*/ #include "blobs.h" #include "scrollview.h" /*---------------------------------------------------------------------- T y p e s ----------------------------------------------------------------------*/ struct SPLIT { SPLIT() : point1(NULL), point2(NULL) {} SPLIT(EDGEPT* pt1, EDGEPT* pt2) : point1(pt1), point2(pt2) {} // Returns the bounding box of all the points in the split. TBOX bounding_box() const; // Returns the bounding box of the outline from point1 to point2. TBOX Box12() const { return point1->SegmentBox(point2); } // Returns the bounding box of the outline from point1 to point1. TBOX Box21() const { return point2->SegmentBox(point1); } // Returns the bounding box of the out // Hides the SPLIT so the outlines appear not to be cut by it. void Hide() const; // Undoes hide, so the outlines are cut by the SPLIT. void Reveal() const; // Returns true if the given EDGEPT is used by this SPLIT, checking only // the EDGEPT pointer, not the coordinates. bool UsesPoint(const EDGEPT* point) const { return point1 == point || point2 == point; } // Returns true if the other SPLIT has any position shared with *this. bool SharesPosition(const SPLIT& other) const { return point1->EqualPos(*other.point1) || point1->EqualPos(*other.point2) || point2->EqualPos(*other.point1) || point2->EqualPos(*other.point2); } // Returns true if both points are contained within the blob. bool ContainedByBlob(const TBLOB& blob) const { return blob.Contains(point1->pos) && blob.Contains(point2->pos); } // Returns true if both points are contained within the outline. bool ContainedByOutline(const TESSLINE& outline) const { return outline.Contains(point1->pos) && outline.Contains(point2->pos); } // Compute a split priority based on the bounding boxes of the parts. // The arguments here are config parameters defined in Wordrec. Add chop_ // to the beginning of the name. float FullPriority(int xmin, int xmax, double overlap_knob, int centered_maxwidth, double center_knob, double width_change_knob) const; // Returns true if *this SPLIT appears OK in the sense that it does not cross // any outlines and does not chop off any ridiculously small pieces. bool IsHealthy(const TBLOB& blob, int min_points, int min_area) const; // Returns true if the split generates a small chunk in terms of either area // or number of points. bool IsLittleChunk(int min_points, int min_area) const; void Print() const; #ifndef GRAPHICS_DISABLED // Draws the split in the given window. void Mark(ScrollView* window) const; #endif // Creates two outlines out of one by splitting the original one in half. // Inserts the resulting outlines into the given list. void SplitOutlineList(TESSLINE* outlines) const; // Makes a split between these two edge points, but does not affect the // outlines to which they belong. void SplitOutline() const; // Undoes the effect of SplitOutlineList, correcting the outlines for undoing // the split, but possibly leaving some duplicate outlines. void UnsplitOutlineList(TBLOB* blob) const; // Removes the split that was put between these two points. void UnsplitOutlines() const; EDGEPT *point1; EDGEPT *point2; }; /*---------------------------------------------------------------------- V a r i a b l e s ----------------------------------------------------------------------*/ extern BOOL_VAR_H(wordrec_display_splits, 0, "Display splits"); /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ EDGEPT *make_edgept(int x, int y, EDGEPT *next, EDGEPT *prev); void remove_edgept(EDGEPT *point); #endif tesseract-3.04.01/ccstruct/statistc.cpp000066400000000000000000000654451266071204500200710ustar00rootroot00000000000000/********************************************************************** * File: statistc.c (Formerly stats.c) * Description: Simple statistical package for integer values. * Author: Ray Smith * Created: Mon Feb 04 16:56:05 GMT 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "statistc.h" #include #include #include #include "helpers.h" #include "scrollview.h" #include "tprintf.h" using tesseract::KDPairInc; /********************************************************************** * STATS::STATS * * Construct a new stats element by allocating and zeroing the memory. **********************************************************************/ STATS::STATS(inT32 min_bucket_value, inT32 max_bucket_value_plus_1) { if (max_bucket_value_plus_1 <= min_bucket_value) { min_bucket_value = 0; max_bucket_value_plus_1 = 1; } rangemin_ = min_bucket_value; // setup rangemax_ = max_bucket_value_plus_1; buckets_ = new inT32[rangemax_ - rangemin_]; clear(); } STATS::STATS() { rangemax_ = 0; rangemin_ = 0; buckets_ = NULL; } /********************************************************************** * STATS::set_range * * Alter the range on an existing stats element. **********************************************************************/ bool STATS::set_range(inT32 min_bucket_value, inT32 max_bucket_value_plus_1) { if (max_bucket_value_plus_1 <= min_bucket_value) { return false; } if (rangemax_ - rangemin_ != max_bucket_value_plus_1 - min_bucket_value) { delete [] buckets_; buckets_ = new inT32[max_bucket_value_plus_1 - min_bucket_value]; } rangemin_ = min_bucket_value; // setup rangemax_ = max_bucket_value_plus_1; clear(); // zero it return true; } /********************************************************************** * STATS::clear * * Clear out the STATS class by zeroing all the buckets. **********************************************************************/ void STATS::clear() { // clear out buckets total_count_ = 0; if (buckets_ != NULL) memset(buckets_, 0, (rangemax_ - rangemin_) * sizeof(buckets_[0])); } /********************************************************************** * STATS::~STATS * * Destructor for a stats class. **********************************************************************/ STATS::~STATS () { if (buckets_ != NULL) { delete [] buckets_; buckets_ = NULL; } } /********************************************************************** * STATS::add * * Add a set of samples to (or delete from) a pile. **********************************************************************/ void STATS::add(inT32 value, inT32 count) { if (buckets_ == NULL) { return; } value = ClipToRange(value, rangemin_, rangemax_ - 1); buckets_[value - rangemin_] += count; total_count_ += count; // keep count of total } /********************************************************************** * STATS::mode * * Find the mode of a stats class. **********************************************************************/ inT32 STATS::mode() const { // get mode of samples if (buckets_ == NULL) { return rangemin_; } inT32 max = buckets_[0]; // max cell count inT32 maxindex = 0; // index of max for (int index = rangemax_ - rangemin_ - 1; index > 0; --index) { if (buckets_[index] > max) { max = buckets_[index]; // find biggest maxindex = index; } } return maxindex + rangemin_; // index of biggest } /********************************************************************** * STATS::mean * * Find the mean of a stats class. **********************************************************************/ double STATS::mean() const { //get mean of samples if (buckets_ == NULL || total_count_ <= 0) { return static_cast(rangemin_); } inT64 sum = 0; for (int index = rangemax_ - rangemin_ - 1; index >= 0; --index) { sum += static_cast(index) * buckets_[index]; } return static_cast(sum) / total_count_ + rangemin_; } /********************************************************************** * STATS::sd * * Find the standard deviation of a stats class. **********************************************************************/ double STATS::sd() const { //standard deviation if (buckets_ == NULL || total_count_ <= 0) { return 0.0; } inT64 sum = 0; double sqsum = 0.0; for (int index = rangemax_ - rangemin_ - 1; index >= 0; --index) { sum += static_cast(index) * buckets_[index]; sqsum += static_cast(index) * index * buckets_[index]; } double variance = static_cast(sum) / total_count_; variance = sqsum / total_count_ - variance * variance; if (variance > 0.0) return sqrt(variance); return 0.0; } /********************************************************************** * STATS::ile * * Returns the fractile value such that frac fraction (in [0,1]) of samples * has a value less than the return value. **********************************************************************/ double STATS::ile(double frac) const { if (buckets_ == NULL || total_count_ == 0) { return static_cast(rangemin_); } #if 0 // TODO(rays) The existing code doesn't seem to be doing the right thing // with target a double but this substitute crashes the code that uses it. // Investigate and fix properly. int target = IntCastRounded(frac * total_count_); target = ClipToRange(target, 1, total_count_); #else double target = frac * total_count_; target = ClipToRange(target, 1.0, static_cast(total_count_)); #endif int sum = 0; int index = 0; for (index = 0; index < rangemax_ - rangemin_ && sum < target; sum += buckets_[index++]); if (index > 0) { ASSERT_HOST(buckets_[index - 1] > 0); return rangemin_ + index - static_cast(sum - target) / buckets_[index - 1]; } else { return static_cast(rangemin_); } } /********************************************************************** * STATS::min_bucket * * Find REAL minimum bucket - ile(0.0) isn't necessarily correct **********************************************************************/ inT32 STATS::min_bucket() const { // Find min if (buckets_ == NULL || total_count_ == 0) { return rangemin_; } inT32 min = 0; for (min = 0; (min < rangemax_ - rangemin_) && (buckets_[min] == 0); min++); return rangemin_ + min; } /********************************************************************** * STATS::max_bucket * * Find REAL maximum bucket - ile(1.0) isn't necessarily correct **********************************************************************/ inT32 STATS::max_bucket() const { // Find max if (buckets_ == NULL || total_count_ == 0) { return rangemin_; } inT32 max; for (max = rangemax_ - rangemin_ - 1; max > 0 && buckets_[max] == 0; max--); return rangemin_ + max; } /********************************************************************** * STATS::median * * Finds a more useful estimate of median than ile(0.5). * * Overcomes a problem with ile() - if the samples are, for example, * 6,6,13,14 ile(0.5) return 7.0 - when a more useful value would be midway * between 6 and 13 = 9.5 **********************************************************************/ double STATS::median() const { //get median if (buckets_ == NULL) { return static_cast(rangemin_); } double median = ile(0.5); int median_pile = static_cast(floor(median)); if ((total_count_ > 1) && (pile_count(median_pile) == 0)) { inT32 min_pile; inT32 max_pile; /* Find preceding non zero pile */ for (min_pile = median_pile; pile_count(min_pile) == 0; min_pile--); /* Find following non zero pile */ for (max_pile = median_pile; pile_count(max_pile) == 0; max_pile++); median = (min_pile + max_pile) / 2.0; } return median; } /********************************************************************** * STATS::local_min * * Return TRUE if this point is a local min. **********************************************************************/ bool STATS::local_min(inT32 x) const { if (buckets_ == NULL) { return false; } x = ClipToRange(x, rangemin_, rangemax_ - 1) - rangemin_; if (buckets_[x] == 0) return true; inT32 index; // table index for (index = x - 1; index >= 0 && buckets_[index] == buckets_[x]; --index); if (index >= 0 && buckets_[index] < buckets_[x]) return false; for (index = x + 1; index < rangemax_ - rangemin_ && buckets_[index] == buckets_[x]; ++index); if (index < rangemax_ - rangemin_ && buckets_[index] < buckets_[x]) return false; else return true; } /********************************************************************** * STATS::smooth * * Apply a triangular smoothing filter to the stats. * This makes the modes a bit more useful. * The factor gives the height of the triangle, i.e. the weight of the * centre. **********************************************************************/ void STATS::smooth(inT32 factor) { if (buckets_ == NULL || factor < 2) { return; } STATS result(rangemin_, rangemax_); int entrycount = rangemax_ - rangemin_; for (int entry = 0; entry < entrycount; entry++) { //centre weight int count = buckets_[entry] * factor; for (int offset = 1; offset < factor; offset++) { if (entry - offset >= 0) count += buckets_[entry - offset] * (factor - offset); if (entry + offset < entrycount) count += buckets_[entry + offset] * (factor - offset); } result.add(entry + rangemin_, count); } total_count_ = result.total_count_; memcpy(buckets_, result.buckets_, entrycount * sizeof(buckets_[0])); } /********************************************************************** * STATS::cluster * * Cluster the samples into max_cluster clusters. * Each call runs one iteration. The array of clusters must be * max_clusters+1 in size as cluster 0 is used to indicate which samples * have been used. * The return value is the current number of clusters. **********************************************************************/ inT32 STATS::cluster(float lower, // thresholds float upper, float multiple, // distance threshold inT32 max_clusters, // max no to make STATS *clusters) { // array of clusters BOOL8 new_cluster; // added one float *centres; // cluster centres inT32 entry; // bucket index inT32 cluster; // cluster index inT32 best_cluster; // one to assign to inT32 new_centre = 0; // residual mode inT32 new_mode; // pile count of new_centre inT32 count; // pile to place float dist; // from cluster float min_dist; // from best_cluster inT32 cluster_count; // no of clusters if (buckets_ == NULL || max_clusters < 1) return 0; centres = new float[max_clusters + 1]; for (cluster_count = 1; cluster_count <= max_clusters && clusters[cluster_count].buckets_ != NULL && clusters[cluster_count].total_count_ > 0; cluster_count++) { centres[cluster_count] = static_cast(clusters[cluster_count].ile(0.5)); new_centre = clusters[cluster_count].mode(); for (entry = new_centre - 1; centres[cluster_count] - entry < lower && entry >= rangemin_ && pile_count(entry) <= pile_count(entry + 1); entry--) { count = pile_count(entry) - clusters[0].pile_count(entry); if (count > 0) { clusters[cluster_count].add(entry, count); clusters[0].add (entry, count); } } for (entry = new_centre + 1; entry - centres[cluster_count] < lower && entry < rangemax_ && pile_count(entry) <= pile_count(entry - 1); entry++) { count = pile_count(entry) - clusters[0].pile_count(entry); if (count > 0) { clusters[cluster_count].add(entry, count); clusters[0].add(entry, count); } } } cluster_count--; if (cluster_count == 0) { clusters[0].set_range(rangemin_, rangemax_); } do { new_cluster = FALSE; new_mode = 0; for (entry = 0; entry < rangemax_ - rangemin_; entry++) { count = buckets_[entry] - clusters[0].buckets_[entry]; //remaining pile if (count > 0) { //any to handle min_dist = static_cast(MAX_INT32); best_cluster = 0; for (cluster = 1; cluster <= cluster_count; cluster++) { dist = entry + rangemin_ - centres[cluster]; //find distance if (dist < 0) dist = -dist; if (dist < min_dist) { min_dist = dist; //find least best_cluster = cluster; } } if (min_dist > upper //far enough for new && (best_cluster == 0 || entry + rangemin_ > centres[best_cluster] * multiple || entry + rangemin_ < centres[best_cluster] / multiple)) { if (count > new_mode) { new_mode = count; new_centre = entry + rangemin_; } } } } // need new and room if (new_mode > 0 && cluster_count < max_clusters) { cluster_count++; new_cluster = TRUE; if (!clusters[cluster_count].set_range(rangemin_, rangemax_)) { delete [] centres; return 0; } centres[cluster_count] = static_cast(new_centre); clusters[cluster_count].add(new_centre, new_mode); clusters[0].add(new_centre, new_mode); for (entry = new_centre - 1; centres[cluster_count] - entry < lower && entry >= rangemin_ && pile_count (entry) <= pile_count(entry + 1); entry--) { count = pile_count(entry) - clusters[0].pile_count(entry); if (count > 0) { clusters[cluster_count].add(entry, count); clusters[0].add(entry, count); } } for (entry = new_centre + 1; entry - centres[cluster_count] < lower && entry < rangemax_ && pile_count (entry) <= pile_count(entry - 1); entry++) { count = pile_count(entry) - clusters[0].pile_count(entry); if (count > 0) { clusters[cluster_count].add(entry, count); clusters[0].add (entry, count); } } centres[cluster_count] = static_cast(clusters[cluster_count].ile(0.5)); } } while (new_cluster && cluster_count < max_clusters); delete [] centres; return cluster_count; } // Helper tests that the current index is still part of the peak and gathers // the data into the peak, returning false when the peak is ended. // src_buckets[index] - used_buckets[index] is the unused part of the histogram. // prev_count is the histogram count of the previous index on entry and is // updated to the current index on return. // total_count and total_value are accumulating the mean of the peak. static bool GatherPeak(int index, const int* src_buckets, int* used_buckets, int* prev_count, int* total_count, double* total_value) { int pile_count = src_buckets[index] - used_buckets[index]; if (pile_count <= *prev_count && pile_count > 0) { // Accumulate count and index.count product. *total_count += pile_count; *total_value += index * pile_count; // Mark this index as used used_buckets[index] = src_buckets[index]; *prev_count = pile_count; return true; } else { return false; } } // Finds (at most) the top max_modes modes, well actually the whole peak around // each mode, returning them in the given modes vector as a pair in order of decreasing total count. // Since the mean is the key and the count the data in the pair, a single call // to sort on the output will re-sort by increasing mean of peak if that is // more useful than decreasing total count. // Returns the actual number of modes found. int STATS::top_n_modes(int max_modes, GenericVector >* modes) const { if (max_modes <= 0) return 0; int src_count = rangemax_ - rangemin_; // Used copies the counts in buckets_ as they get used. STATS used(rangemin_, rangemax_); modes->truncate(0); // Total count of the smallest peak found so far. int least_count = 1; // Mode that is used as a seed for each peak int max_count = 0; do { // Find an unused mode. max_count = 0; int max_index = 0; for (int src_index = 0; src_index < src_count; src_index++) { int pile_count = buckets_[src_index] - used.buckets_[src_index]; if (pile_count > max_count) { max_count = pile_count; max_index = src_index; } } if (max_count > 0) { // Copy the bucket count to used so it doesn't get found again. used.buckets_[max_index] = max_count; // Get the entire peak. double total_value = max_index * max_count; int total_count = max_count; int prev_pile = max_count; for (int offset = 1; max_index + offset < src_count; ++offset) { if (!GatherPeak(max_index + offset, buckets_, used.buckets_, &prev_pile, &total_count, &total_value)) break; } prev_pile = buckets_[max_index]; for (int offset = 1; max_index - offset >= 0; ++offset) { if (!GatherPeak(max_index - offset, buckets_, used.buckets_, &prev_pile, &total_count, &total_value)) break; } if (total_count > least_count || modes->size() < max_modes) { // We definitely want this mode, so if we have enough discard the least. if (modes->size() == max_modes) modes->truncate(max_modes - 1); int target_index = 0; // Linear search for the target insertion point. while (target_index < modes->size() && (*modes)[target_index].data >= total_count) ++target_index; float peak_mean = static_cast(total_value / total_count + rangemin_); modes->insert(KDPairInc(peak_mean, total_count), target_index); least_count = modes->back().data; } } } while (max_count > 0); return modes->size(); } /********************************************************************** * STATS::print * * Prints a summary and table of the histogram. **********************************************************************/ void STATS::print() const { if (buckets_ == NULL) { return; } inT32 min = min_bucket() - rangemin_; inT32 max = max_bucket() - rangemin_; int num_printed = 0; for (int index = min; index <= max; index++) { if (buckets_[index] != 0) { tprintf("%4d:%-3d ", rangemin_ + index, buckets_[index]); if (++num_printed % 8 == 0) tprintf ("\n"); } } tprintf ("\n"); print_summary(); } /********************************************************************** * STATS::print_summary * * Print a summary of the stats. **********************************************************************/ void STATS::print_summary() const { if (buckets_ == NULL) { return; } inT32 min = min_bucket(); inT32 max = max_bucket(); tprintf("Total count=%d\n", total_count_); tprintf("Min=%.2f Really=%d\n", ile(0.0), min); tprintf("Lower quartile=%.2f\n", ile(0.25)); tprintf("Median=%.2f, ile(0.5)=%.2f\n", median(), ile(0.5)); tprintf("Upper quartile=%.2f\n", ile(0.75)); tprintf("Max=%.2f Really=%d\n", ile(1.0), max); tprintf("Range=%d\n", max + 1 - min); tprintf("Mean= %.2f\n", mean()); tprintf("SD= %.2f\n", sd()); } /********************************************************************** * STATS::plot * * Draw a histogram of the stats table. **********************************************************************/ #ifndef GRAPHICS_DISABLED void STATS::plot(ScrollView* window, // to draw in float xorigin, // bottom left float yorigin, float xscale, // one x unit float yscale, // one y unit ScrollView::Color colour) const { // colour to draw in if (buckets_ == NULL) { return; } window->Pen(colour); for (int index = 0; index < rangemax_ - rangemin_; index++) { window->Rectangle( xorigin + xscale * index, yorigin, xorigin + xscale * (index + 1), yorigin + yscale * buckets_[index]); } } #endif /********************************************************************** * STATS::plotline * * Draw a histogram of the stats table. (Line only) **********************************************************************/ #ifndef GRAPHICS_DISABLED void STATS::plotline(ScrollView* window, // to draw in float xorigin, // bottom left float yorigin, float xscale, // one x unit float yscale, // one y unit ScrollView::Color colour) const { // colour to draw in if (buckets_ == NULL) { return; } window->Pen(colour); window->SetCursor(xorigin, yorigin + yscale * buckets_[0]); for (int index = 0; index < rangemax_ - rangemin_; index++) { window->DrawTo(xorigin + xscale * index, yorigin + yscale * buckets_[index]); } } #endif /********************************************************************** * choose_nth_item * * Returns the index of what would b the nth item in the array * if the members were sorted, without actually sorting. **********************************************************************/ inT32 choose_nth_item(inT32 index, float *array, inT32 count) { inT32 next_sample; // next one to do inT32 next_lesser; // space for new inT32 prev_greater; // last one saved inT32 equal_count; // no of equal ones float pivot; // proposed median float sample; // current sample if (count <= 1) return 0; if (count == 2) { if (array[0] < array[1]) { return index >= 1 ? 1 : 0; } else { return index >= 1 ? 0 : 1; } } else { if (index < 0) index = 0; // ensure legal else if (index >= count) index = count - 1; equal_count = (inT32) (rand() % count); pivot = array[equal_count]; // fill gap array[equal_count] = array[0]; next_lesser = 0; prev_greater = count; equal_count = 1; for (next_sample = 1; next_sample < prev_greater;) { sample = array[next_sample]; if (sample < pivot) { // shuffle array[next_lesser++] = sample; next_sample++; } else if (sample > pivot) { prev_greater--; // juggle array[next_sample] = array[prev_greater]; array[prev_greater] = sample; } else { equal_count++; next_sample++; } } for (next_sample = next_lesser; next_sample < prev_greater;) array[next_sample++] = pivot; if (index < next_lesser) return choose_nth_item (index, array, next_lesser); else if (index < prev_greater) return next_lesser; // in equal bracket else return choose_nth_item (index - prev_greater, array + prev_greater, count - prev_greater) + prev_greater; } } /********************************************************************** * choose_nth_item * * Returns the index of what would be the nth item in the array * if the members were sorted, without actually sorting. **********************************************************************/ inT32 choose_nth_item(inT32 index, void *array, inT32 count, size_t size, int (*compar)(const void*, const void*)) { int result; // of compar inT32 next_sample; // next one to do inT32 next_lesser; // space for new inT32 prev_greater; // last one saved inT32 equal_count; // no of equal ones inT32 pivot; // proposed median if (count <= 1) return 0; if (count == 2) { if (compar (array, (char *) array + size) < 0) { return index >= 1 ? 1 : 0; } else { return index >= 1 ? 0 : 1; } } if (index < 0) index = 0; // ensure legal else if (index >= count) index = count - 1; pivot = (inT32) (rand () % count); swap_entries (array, size, pivot, 0); next_lesser = 0; prev_greater = count; equal_count = 1; for (next_sample = 1; next_sample < prev_greater;) { result = compar ((char *) array + size * next_sample, (char *) array + size * next_lesser); if (result < 0) { swap_entries (array, size, next_lesser++, next_sample++); // shuffle } else if (result > 0) { prev_greater--; swap_entries(array, size, prev_greater, next_sample); } else { equal_count++; next_sample++; } } if (index < next_lesser) return choose_nth_item (index, array, next_lesser, size, compar); else if (index < prev_greater) return next_lesser; // in equal bracket else return choose_nth_item (index - prev_greater, (char *) array + size * prev_greater, count - prev_greater, size, compar) + prev_greater; } /********************************************************************** * swap_entries * * Swap 2 entries of arbitrary size in-place in a table. **********************************************************************/ void swap_entries(void *array, // array of entries size_t size, // size of entry inT32 index1, // entries to swap inT32 index2) { char tmp; char *ptr1; // to entries char *ptr2; size_t count; // of bytes ptr1 = reinterpret_cast(array) + index1 * size; ptr2 = reinterpret_cast(array) + index2 * size; for (count = 0; count < size; count++) { tmp = *ptr1; *ptr1++ = *ptr2; *ptr2++ = tmp; // tedious! } } tesseract-3.04.01/ccstruct/statistc.h000066400000000000000000000161221266071204500175220ustar00rootroot00000000000000/********************************************************************** * File: statistc.h (Formerly stats.h) * Description: Class description for STATS class. * Author: Ray Smith * Created: Mon Feb 04 16:19:07 GMT 1991 * * (C) Copyright 1991, 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 TESSERACT_CCSTRUCT_STATISTC_H_ #define TESSERACT_CCSTRUCT_STATISTC_H_ #include #include "host.h" #include "kdpair.h" #include "scrollview.h" template class GenericVector; // Simple histogram-based statistics for integer values in a known // range, such that the range is small compared to the number of samples. class STATS { public: // The histogram buckets are in the range // [min_bucket_value, max_bucket_value_plus_1 - 1] i.e. // [min_bucket_value, max_bucket_value]. // Any data under min_bucket value is silently mapped to min_bucket_value, // and likewise, any data over max_bucket_value is silently mapped to // max_bucket_value. // In the internal array, min_bucket_value maps to 0 and // max_bucket_value_plus_1 - min_bucket_value to the array size. // TODO(rays) This is ugly. Convert the second argument to // max_bucket_value and all the code that uses it. STATS(inT32 min_bucket_value, inT32 max_bucket_value_plus_1); STATS(); // empty for arrays ~STATS(); // (Re)Sets the range and clears the counts. // See the constructor for info on max and min values. bool set_range(inT32 min_bucket_value, inT32 max_bucket_value_plus_1); void clear(); // empty buckets void add(inT32 value, inT32 count); // "Accessors" return various statistics on the data. inT32 mode() const; // get mode of samples double mean() const; // get mean of samples double sd() const; // standard deviation // Returns the fractile value such that frac fraction (in [0,1]) of samples // has a value less than the return value. double ile(double frac) const; // Returns the minimum used entry in the histogram (ie the minimum of the // data, NOT the minimum of the supplied range, nor is it an index.) // Would normally be called min(), but that is a reserved word in VC++. inT32 min_bucket() const; // Find min // Returns the maximum used entry in the histogram (ie the maximum of the // data, NOT the maximum of the supplied range, nor is it an index.) inT32 max_bucket() const; // Find max // Finds a more useful estimate of median than ile(0.5). // Overcomes a problem with ile() - if the samples are, for example, // 6,6,13,14 ile(0.5) return 7.0 - when a more useful value would be midway // between 6 and 13 = 9.5 double median() const; // get median of samples // Returns the count of the given value. inT32 pile_count(inT32 value ) const { if (value <= rangemin_) return buckets_[0]; if (value >= rangemax_ - 1) return buckets_[rangemax_ - rangemin_ - 1]; return buckets_[value - rangemin_]; } // Returns the total count of all buckets. inT32 get_total() const { return total_count_; // total of all piles } // Returns true if x is a local min. bool local_min(inT32 x) const; // Apply a triangular smoothing filter to the stats. // This makes the modes a bit more useful. // The factor gives the height of the triangle, i.e. the weight of the // centre. void smooth(inT32 factor); // Cluster the samples into max_cluster clusters. // Each call runs one iteration. The array of clusters must be // max_clusters+1 in size as cluster 0 is used to indicate which samples // have been used. // The return value is the current number of clusters. inT32 cluster(float lower, // thresholds float upper, float multiple, // distance threshold inT32 max_clusters, // max no to make STATS *clusters); // array of clusters // Finds (at most) the top max_modes modes, well actually the whole peak around // each mode, returning them in the given modes vector as a pair in order of decreasing total count. // Since the mean is the key and the count the data in the pair, a single call // to sort on the output will re-sort by increasing mean of peak if that is // more useful than decreasing total count. // Returns the actual number of modes found. int top_n_modes( int max_modes, GenericVector >* modes) const; // Prints a summary and table of the histogram. void print() const; // Prints summary stats only of the histogram. void print_summary() const; #ifndef GRAPHICS_DISABLED // Draws the histogram as a series of rectangles. void plot(ScrollView* window, // window to draw in float xorigin, // origin of histo float yorigin, // gram float xscale, // size of one unit float yscale, // size of one uint ScrollView::Color colour) const; // colour to draw in // Draws a line graph of the histogram. void plotline(ScrollView* window, // window to draw in float xorigin, // origin of histo float yorigin, // gram float xscale, // size of one unit float yscale, // size of one uint ScrollView::Color colour) const; // colour to draw in #endif // GRAPHICS_DISABLED private: inT32 rangemin_; // min of range // rangemax_ is not well named as it is really one past the max. inT32 rangemax_; // max of range inT32 total_count_; // no of samples inT32* buckets_; // array of cells }; // Returns the nth ordered item from the array, as if they were // ordered, but without ordering them, in linear time. // The array does get shuffled! inT32 choose_nth_item(inT32 index, // index to choose float *array, // array of items inT32 count); // no of items // Generic version uses a defined comparator (with qsort semantics). inT32 choose_nth_item(inT32 index, // index to choose void *array, // array of items inT32 count, // no of items size_t size, // element size int (*compar)(const void*, const void*)); // comparator // Swaps 2 entries in an array in-place. void swap_entries(void *array, // array of entries size_t size, // size of entry inT32 index1, // entries to swap inT32 index2); #endif // TESSERACT_CCSTRUCT_STATISTC_H_ tesseract-3.04.01/ccstruct/stepblob.cpp000066400000000000000000000473101266071204500200340ustar00rootroot00000000000000/********************************************************************** * File: stepblob.cpp (Formerly cblob.c) * Description: Code for C_BLOB class. * Author: Ray Smith * Created: Tue Oct 08 10:41:13 BST 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include "stepblob.h" #include "allheaders.h" // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif // Max perimeter to width ratio for a baseline position above box bottom. const double kMaxPerimeterWidthRatio = 8.0; ELISTIZE (C_BLOB) /********************************************************************** * position_outline * * Position the outline in the given list at the relevant place * according to its nesting. **********************************************************************/ static void position_outline( //put in place C_OUTLINE *outline, //thing to place C_OUTLINE_LIST *destlist //desstination list ) { C_OUTLINE *dest_outline; //outline from dest list C_OUTLINE_IT it = destlist; //iterator //iterator on children C_OUTLINE_IT child_it = outline->child (); if (!it.empty ()) { do { dest_outline = it.data (); //get destination //encloses dest if (*dest_outline < *outline) { //take off list dest_outline = it.extract (); //put this in place it.add_after_then_move (outline); //make it a child child_it.add_to_end (dest_outline); while (!it.at_last ()) { it.forward (); //do rest of list //check for other children dest_outline = it.data (); if (*dest_outline < *outline) { //take off list dest_outline = it.extract (); child_it.add_to_end (dest_outline); //make it a child if (it.empty ()) break; } } return; //finished } //enclosed by dest else if (*outline < *dest_outline) { position_outline (outline, dest_outline->child ()); //place in child list return; //finished } it.forward (); } while (!it.at_first ()); } it.add_to_end (outline); //at outer level } /********************************************************************** * plot_outline_list * * Draw a list of outlines in the given colour and their children * in the child colour. **********************************************************************/ #ifndef GRAPHICS_DISABLED static void plot_outline_list( //draw outlines C_OUTLINE_LIST *list, //outline to draw ScrollView* window, //window to draw in ScrollView::Color colour, //colour to use ScrollView::Color child_colour //colour of children ) { C_OUTLINE *outline; //current outline C_OUTLINE_IT it = list; //iterator for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { outline = it.data (); //draw it outline->plot (window, colour); if (!outline->child ()->empty ()) plot_outline_list (outline->child (), window, child_colour, child_colour); } } // Draws the outlines in the given colour, and child_colour, normalized // using the given denorm, making use of sub-pixel accurate information // if available. static void plot_normed_outline_list(const DENORM& denorm, C_OUTLINE_LIST *list, ScrollView::Color colour, ScrollView::Color child_colour, ScrollView* window) { C_OUTLINE_IT it(list); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { C_OUTLINE* outline = it.data(); outline->plot_normed(denorm, colour, window); if (!outline->child()->empty()) plot_normed_outline_list(denorm, outline->child(), child_colour, child_colour, window); } } #endif /********************************************************************** * reverse_outline_list * * Reverse a list of outlines and their children. **********************************************************************/ static void reverse_outline_list(C_OUTLINE_LIST *list) { C_OUTLINE_IT it = list; // iterator for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { C_OUTLINE* outline = it.data(); outline->reverse(); // reverse it outline->set_flag(COUT_INVERSE, TRUE); if (!outline->child()->empty()) reverse_outline_list(outline->child()); } } /********************************************************************** * C_BLOB::C_BLOB * * Constructor to build a C_BLOB from a list of C_OUTLINEs. * The C_OUTLINEs are not copied so the source list is emptied. * The C_OUTLINEs are nested correctly in the blob. **********************************************************************/ C_BLOB::C_BLOB(C_OUTLINE_LIST *outline_list) { for (C_OUTLINE_IT ol_it(outline_list); !ol_it.empty(); ol_it.forward()) { C_OUTLINE* outline = ol_it.extract(); // Position this outline in appropriate position in the hierarchy. position_outline(outline, &outlines); } CheckInverseFlagAndDirection(); } // Simpler constructor to build a blob from a single outline that has // already been fully initialized. C_BLOB::C_BLOB(C_OUTLINE* outline) { C_OUTLINE_IT it(&outlines); it.add_to_end(outline); } // Builds a set of one or more blobs from a list of outlines. // Input: one outline on outline_list contains all the others, but the // nesting and order are undefined. // If good_blob is true, the blob is added to good_blobs_it, unless // an illegal (generation-skipping) parent-child relationship is found. // If so, the parent blob goes to bad_blobs_it, and the immediate children // are promoted to the top level, recursively being sent to good_blobs_it. // If good_blob is false, all created blobs will go to the bad_blobs_it. // Output: outline_list is empty. One or more blobs are added to // good_blobs_it and/or bad_blobs_it. void C_BLOB::ConstructBlobsFromOutlines(bool good_blob, C_OUTLINE_LIST* outline_list, C_BLOB_IT* good_blobs_it, C_BLOB_IT* bad_blobs_it) { // List of top-level outlines with correctly nested children. C_OUTLINE_LIST nested_outlines; for (C_OUTLINE_IT ol_it(outline_list); !ol_it.empty(); ol_it.forward()) { C_OUTLINE* outline = ol_it.extract(); // Position this outline in appropriate position in the hierarchy. position_outline(outline, &nested_outlines); } // Check for legal nesting and reassign as required. for (C_OUTLINE_IT ol_it(&nested_outlines); !ol_it.empty(); ol_it.forward()) { C_OUTLINE* outline = ol_it.extract(); bool blob_is_good = good_blob; if (!outline->IsLegallyNested()) { // The blob is illegally nested. // Mark it bad, and add all its children to the top-level list. blob_is_good = false; ol_it.add_list_after(outline->child()); } C_BLOB* blob = new C_BLOB(outline); // Set inverse flag and reverse if needed. blob->CheckInverseFlagAndDirection(); // Put on appropriate list. if (!blob_is_good && bad_blobs_it != NULL) bad_blobs_it->add_after_then_move(blob); else good_blobs_it->add_after_then_move(blob); } } // Sets the COUT_INVERSE flag appropriately on the outlines and their // children recursively, reversing the outlines if needed so that // everything has an anticlockwise top-level. void C_BLOB::CheckInverseFlagAndDirection() { C_OUTLINE_IT ol_it(&outlines); for (ol_it.mark_cycle_pt(); !ol_it.cycled_list(); ol_it.forward()) { C_OUTLINE* outline = ol_it.data(); if (outline->turn_direction() < 0) { outline->reverse(); reverse_outline_list(outline->child()); outline->set_flag(COUT_INVERSE, TRUE); } else { outline->set_flag(COUT_INVERSE, FALSE); } } } // Build and return a fake blob containing a single fake outline with no // steps. C_BLOB* C_BLOB::FakeBlob(const TBOX& box) { C_OUTLINE_LIST outlines; C_OUTLINE::FakeOutline(box, &outlines); return new C_BLOB(&outlines); } /********************************************************************** * C_BLOB::bounding_box * * Return the bounding box of the blob. **********************************************************************/ TBOX C_BLOB::bounding_box() const { // bounding box C_OUTLINE *outline; // current outline // This is a read-only iteration of the outlines. C_OUTLINE_IT it = const_cast(&outlines); TBOX box; // bounding box for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { outline = it.data (); box += outline->bounding_box (); } return box; } /********************************************************************** * C_BLOB::area * * Return the area of the blob. **********************************************************************/ inT32 C_BLOB::area() { //area C_OUTLINE *outline; //current outline C_OUTLINE_IT it = &outlines; //outlines of blob inT32 total; //total area total = 0; for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { outline = it.data (); total += outline->area (); } return total; } /********************************************************************** * C_BLOB::perimeter * * Return the perimeter of the top and 2nd level outlines. **********************************************************************/ inT32 C_BLOB::perimeter() { C_OUTLINE *outline; // current outline C_OUTLINE_IT it = &outlines; // outlines of blob inT32 total; // total perimeter total = 0; for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { outline = it.data(); total += outline->perimeter(); } return total; } /********************************************************************** * C_BLOB::outer_area * * Return the area of the blob. **********************************************************************/ inT32 C_BLOB::outer_area() { //area C_OUTLINE *outline; //current outline C_OUTLINE_IT it = &outlines; //outlines of blob inT32 total; //total area total = 0; for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { outline = it.data (); total += outline->outer_area (); } return total; } /********************************************************************** * C_BLOB::count_transitions * * Return the total x and y maxes and mins in the blob. * Chlid outlines are not counted. **********************************************************************/ inT32 C_BLOB::count_transitions( //area inT32 threshold //on size ) { C_OUTLINE *outline; //current outline C_OUTLINE_IT it = &outlines; //outlines of blob inT32 total; //total area total = 0; for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { outline = it.data (); total += outline->count_transitions (threshold); } return total; } /********************************************************************** * C_BLOB::move * * Move C_BLOB by vector **********************************************************************/ void C_BLOB::move( // reposition blob const ICOORD vec // by vector ) { C_OUTLINE_IT it(&outlines); // iterator for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) it.data ()->move (vec); // move each outline } // Static helper for C_BLOB::rotate to allow recursion of child outlines. void RotateOutlineList(const FCOORD& rotation, C_OUTLINE_LIST* outlines) { C_OUTLINE_LIST new_outlines; C_OUTLINE_IT src_it(outlines); C_OUTLINE_IT dest_it(&new_outlines); while (!src_it.empty()) { C_OUTLINE* old_outline = src_it.extract(); src_it.forward(); C_OUTLINE* new_outline = new C_OUTLINE(old_outline, rotation); if (!old_outline->child()->empty()) { RotateOutlineList(rotation, old_outline->child()); C_OUTLINE_IT child_it(new_outline->child()); child_it.add_list_after(old_outline->child()); } delete old_outline; dest_it.add_to_end(new_outline); } src_it.add_list_after(&new_outlines); } /********************************************************************** * C_BLOB::rotate * * Rotate C_BLOB by rotation. * Warning! has to rebuild all the C_OUTLINEs. **********************************************************************/ void C_BLOB::rotate(const FCOORD& rotation) { RotateOutlineList(rotation, &outlines); } // Helper calls ComputeEdgeOffsets or ComputeBinaryOffsets recursively on the // outline list and its children. static void ComputeEdgeOffsetsOutlineList(int threshold, Pix* pix, C_OUTLINE_LIST *list) { C_OUTLINE_IT it(list); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { C_OUTLINE* outline = it.data(); if (pix != NULL && pixGetDepth(pix) == 8) outline->ComputeEdgeOffsets(threshold, pix); else outline->ComputeBinaryOffsets(); if (!outline->child()->empty()) ComputeEdgeOffsetsOutlineList(threshold, pix, outline->child()); } } // Adds sub-pixel resolution EdgeOffsets for the outlines using greyscale // if the supplied pix is 8-bit or the binary edges if NULL. void C_BLOB::ComputeEdgeOffsets(int threshold, Pix* pix) { ComputeEdgeOffsetsOutlineList(threshold, pix, &outlines); } // Estimates and returns the baseline position based on the shape of the // outlines. // We first find the minimum y-coord (y_mins) at each x-coord within the blob. // If there is a run of some y or y+1 in y_mins that is longer than the total // number of positions at bottom or bottom+1, subject to the additional // condition that at least one side of the y/y+1 run is higher than y+1, so it // is not a local minimum, then y, not the bottom, makes a good candidate // baseline position for this blob. Eg // | ---| // | | // |- -----------| <= Good candidate baseline position. // |- -| // | -| // |---| <= Bottom of blob inT16 C_BLOB::EstimateBaselinePosition() { TBOX box = bounding_box(); int left = box.left(); int width = box.width(); int bottom = box.bottom(); if (outlines.empty() || perimeter() > width * kMaxPerimeterWidthRatio) return bottom; // This is only for non-CJK blobs. // Get the minimum y coordinate at each x-coordinate. GenericVector y_mins; y_mins.init_to_size(width + 1, box.top()); C_OUTLINE_IT it(&outlines); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { C_OUTLINE* outline = it.data(); ICOORD pos = outline->start_pos(); for (int s = 0; s < outline->pathlength(); ++s) { if (pos.y() < y_mins[pos.x() - left]) y_mins[pos.x() - left] = pos.y(); pos += outline->step(s); } } // Find the total extent of the bottom or bottom + 1. int bottom_extent = 0; for (int x = 0; x <= width; ++x) { if (y_mins[x] == bottom || y_mins[x] == bottom + 1) ++bottom_extent; } // Find the lowest run longer than the bottom extent that is not the bottom. int best_min = box.top(); int prev_run = 0; int prev_y = box.top(); int prev_prev_y = box.top(); for (int x = 0; x < width; x += prev_run) { // Find the length of the current run. int y_at_x = y_mins[x]; int run = 1; while (x + run <= width && y_mins[x + run] == y_at_x) ++run; if (y_at_x > bottom + 1) { // Possible contender. int total_run = run; // Find extent of current value or +1 to the right of x. while (x + total_run <= width && (y_mins[x + total_run] == y_at_x || y_mins[x + total_run] == y_at_x + 1)) ++total_run; // At least one end has to be higher so it is not a local max. if (prev_prev_y > y_at_x + 1 || x + total_run > width || y_mins[x + total_run] > y_at_x + 1) { // If the prev_run is at y + 1, then we can add that too. There cannot // be a suitable run at y before that or we would have found it already. if (prev_run > 0 && prev_y == y_at_x + 1) total_run += prev_run; if (total_run > bottom_extent && y_at_x < best_min) { best_min = y_at_x; } } } prev_run = run; prev_prev_y = prev_y; prev_y = y_at_x; } return best_min == box.top() ? bottom : best_min; } static void render_outline_list(C_OUTLINE_LIST *list, int left, int top, Pix* pix) { C_OUTLINE_IT it(list); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { C_OUTLINE* outline = it.data(); outline->render(left, top, pix); if (!outline->child()->empty()) render_outline_list(outline->child(), left, top, pix); } } static void render_outline_list_outline(C_OUTLINE_LIST *list, int left, int top, Pix* pix) { C_OUTLINE_IT it(list); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { C_OUTLINE* outline = it.data(); outline->render_outline(left, top, pix); } } // Returns a Pix rendering of the blob. pixDestroy after use. Pix* C_BLOB::render() { TBOX box = bounding_box(); Pix* pix = pixCreate(box.width(), box.height(), 1); render_outline_list(&outlines, box.left(), box.top(), pix); return pix; } // Returns a Pix rendering of the outline of the blob. (no fill). // pixDestroy after use. Pix* C_BLOB::render_outline() { TBOX box = bounding_box(); Pix* pix = pixCreate(box.width(), box.height(), 1); render_outline_list_outline(&outlines, box.left(), box.top(), pix); return pix; } /********************************************************************** * C_BLOB::plot * * Draw the C_BLOB in the given colour. **********************************************************************/ #ifndef GRAPHICS_DISABLED void C_BLOB::plot(ScrollView* window, // window to draw in ScrollView::Color blob_colour, // main colour ScrollView::Color child_colour) { // for holes plot_outline_list(&outlines, window, blob_colour, child_colour); } // Draws the blob in the given colour, and child_colour, normalized // using the given denorm, making use of sub-pixel accurate information // if available. void C_BLOB::plot_normed(const DENORM& denorm, ScrollView::Color blob_colour, ScrollView::Color child_colour, ScrollView* window) { plot_normed_outline_list(denorm, &outlines, blob_colour, child_colour, window); } #endif tesseract-3.04.01/ccstruct/stepblob.h000066400000000000000000000117761266071204500175100ustar00rootroot00000000000000/********************************************************************** * File: stepblob.h (Formerly cblob.h) * Description: Code for C_BLOB class. * Author: Ray Smith * Created: Tue Oct 08 10:41:13 BST 1991 * * (C) Copyright 1991, 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 STEPBLOB_H #define STEPBLOB_H #include "coutln.h" #include "rect.h" class C_BLOB; struct Pix; ELISTIZEH(C_BLOB) class C_BLOB:public ELIST_LINK { public: C_BLOB() { } explicit C_BLOB(C_OUTLINE_LIST *outline_list); // Simpler constructor to build a blob from a single outline that has // already been fully initialized. explicit C_BLOB(C_OUTLINE* outline); // Builds a set of one or more blobs from a list of outlines. // Input: one outline on outline_list contains all the others, but the // nesting and order are undefined. // If good_blob is true, the blob is added to good_blobs_it, unless // an illegal (generation-skipping) parent-child relationship is found. // If so, the parent blob goes to bad_blobs_it, and the immediate children // are promoted to the top level, recursively being sent to good_blobs_it. // If good_blob is false, all created blobs will go to the bad_blobs_it. // Output: outline_list is empty. One or more blobs are added to // good_blobs_it and/or bad_blobs_it. static void ConstructBlobsFromOutlines(bool good_blob, C_OUTLINE_LIST* outline_list, C_BLOB_IT* good_blobs_it, C_BLOB_IT* bad_blobs_it); // Sets the COUT_INVERSE flag appropriately on the outlines and their // children recursively, reversing the outlines if needed so that // everything has an anticlockwise top-level. void CheckInverseFlagAndDirection(); // Build and return a fake blob containing a single fake outline with no // steps. static C_BLOB* FakeBlob(const TBOX& box); C_OUTLINE_LIST *out_list() { //get outline list return &outlines; } TBOX bounding_box() const; // compute bounding box inT32 area(); //compute area inT32 perimeter(); // Total perimeter of outlines and 1st level children. inT32 outer_area(); //compute area inT32 count_transitions( //count maxima inT32 threshold); //size threshold void move(const ICOORD vec); // repostion blob by vector void rotate(const FCOORD& rotation); // Rotate by given vector. // Adds sub-pixel resolution EdgeOffsets for the outlines using greyscale // if the supplied pix is 8-bit or the binary edges if NULL. void ComputeEdgeOffsets(int threshold, Pix* pix); // Estimates and returns the baseline position based on the shape of the // outlines. inT16 EstimateBaselinePosition(); // Returns a Pix rendering of the blob. pixDestroy after use. Pix* render(); // Returns a Pix rendering of the outline of the blob. (no fill). // pixDestroy after use. Pix* render_outline(); #ifndef GRAPHICS_DISABLED void plot( //draw one ScrollView* window, //window to draw in ScrollView::Color blob_colour, //for outer bits ScrollView::Color child_colour); //for holes // Draws the blob in the given colour, and child_colour, normalized // using the given denorm, making use of sub-pixel accurate information // if available. void plot_normed(const DENORM& denorm, ScrollView::Color blob_colour, ScrollView::Color child_colour, ScrollView* window); #endif // GRAPHICS_DISABLED C_BLOB& operator= (const C_BLOB & source) { if (!outlines.empty ()) outlines.clear(); outlines.deep_copy(&source.outlines, &C_OUTLINE::deep_copy); return *this; } static C_BLOB* deep_copy(const C_BLOB* src) { C_BLOB* blob = new C_BLOB; *blob = *src; return blob; } static int SortByXMiddle(const void *v1, const void *v2) { const C_BLOB* blob1 = *reinterpret_cast(v1); const C_BLOB* blob2 = *reinterpret_cast(v2); return blob1->bounding_box().x_middle() - blob2->bounding_box().x_middle(); } private: C_OUTLINE_LIST outlines; //master elements }; #endif tesseract-3.04.01/ccstruct/vecfuncs.cpp000066400000000000000000000046541266071204500200420ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: vecfuncs.c (Formerly vecfuncs.c) * Description: Blob definition * Author: Mark Seaman, OCR Technology * Created: Fri Oct 27 15:39:52 1989 * Modified: Tue Jul 9 17:44:12 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. * ******************************************************************************** * Revision 5.1 89/07/27 11:47:50 11:47:50 ray () * Added ratings access methods. * This version ready for independent development. */ /*---------------------------------------------------------------------- I n c l u d e s ----------------------------------------------------------------------*/ #include "vecfuncs.h" #include "blobs.h" /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ /********************************************************************** * direction * * Show if the line is going in the positive or negative X direction. **********************************************************************/ int direction(EDGEPT *point) { int dir; /** direction to return **/ EDGEPT *prev; /** prev point **/ EDGEPT *next; /** next point **/ dir = 0; prev = point->prev; next = point->next; if (((prev->pos.x <= point->pos.x) && (point->pos.x < next->pos.x)) || ((prev->pos.x < point->pos.x) && (point->pos.x <= next->pos.x))) dir = 1; if (((prev->pos.x >= point->pos.x) && (point->pos.x > next->pos.x)) || ((prev->pos.x > point->pos.x) && (point->pos.x >= next->pos.x))) dir = -1; return dir; } tesseract-3.04.01/ccstruct/vecfuncs.h000066400000000000000000000051061266071204500175000ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: vecfuncs.h (Formerly vecfuncs.h) * Description: Vector calculations * Author: Mark Seaman, OCR Technology * Created: Wed Dec 20 09:37:18 1989 * Modified: Tue Jul 9 17:44:37 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 VECFUNCS_H #define VECFUNCS_H #include struct EDGEPT; /*---------------------------------------------------------------------- M a c r o s ----------------------------------------------------------------------*/ /********************************************************************** * point_diff * * Return the difference from point (p1) to point (p2). Put the value * into point (p). **********************************************************************/ #define point_diff(p,p1,p2) \ ((p).x = (p1).x - (p2).x, \ (p).y = (p1).y - (p2).y) /********************************************************************** * CROSS * * cross product **********************************************************************/ #define CROSS(a,b) \ ((a).x * (b).y - (a).y * (b).x) /********************************************************************** * SCALAR * * scalar vector product **********************************************************************/ #define SCALAR(a,b) \ ((a).x * (b).x + (a).y * (b).y) /********************************************************************** * LENGTH * * length of vector **********************************************************************/ #define LENGTH(a) \ ((a).x * (a).x + (a).y * (a).y) /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ int direction(EDGEPT *point); #endif tesseract-3.04.01/ccstruct/werd.cpp000066400000000000000000000470051266071204500171640ustar00rootroot00000000000000/********************************************************************** * File: werd.cpp (Formerly word.c) * Description: Code for the WERD class. * Author: Ray Smith * Created: Tue Oct 08 14:32:12 BST 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include "blckerr.h" #include "helpers.h" #include "linlsq.h" #include "werd.h" // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #define FIRST_COLOUR ScrollView::RED //< first rainbow colour #define LAST_COLOUR ScrollView::AQUAMARINE //< last rainbow colour #define CHILD_COLOUR ScrollView::BROWN //< colour of children const ERRCODE CANT_SCALE_EDGESTEPS = "Attempted to scale an edgestep format word"; ELIST2IZE(WERD) /** * WERD::WERD * * Constructor to build a WERD from a list of C_BLOBs. * blob_list The C_BLOBs (in word order) are not copied; * we take its elements and put them in our lists. * blank_count blanks in front of the word * text correct text, outlives this WERD */ WERD::WERD(C_BLOB_LIST *blob_list, uinT8 blank_count, const char *text) : blanks(blank_count), flags(0), script_id_(0), correct(text) { C_BLOB_IT start_it = &cblobs; C_BLOB_IT rej_cblob_it = &rej_cblobs; C_OUTLINE_IT c_outline_it; inT16 inverted_vote = 0; inT16 non_inverted_vote = 0; // Move blob_list's elements into cblobs. start_it.add_list_after(blob_list); /* Set white on black flag for the WERD, moving any duff blobs onto the rej_cblobs list. First, walk the cblobs checking the inverse flag for each outline of each cblob. If a cblob has inconsistent flag settings for its different outlines, move the blob to the reject list. Otherwise, increment the appropriate w-on-b or b-on-w vote for the word. Now set the inversion flag for the WERD by maximum vote. Walk the blobs again, moving any blob whose inversion flag does not agree with the concencus onto the reject list. */ start_it.set_to_list(&cblobs); if (start_it.empty()) return; for (start_it.mark_cycle_pt(); !start_it.cycled_list(); start_it.forward()) { BOOL8 reject_blob = FALSE; BOOL8 blob_inverted; c_outline_it.set_to_list(start_it.data()->out_list()); blob_inverted = c_outline_it.data()->flag(COUT_INVERSE); for (c_outline_it.mark_cycle_pt(); !c_outline_it.cycled_list() && !reject_blob; c_outline_it.forward()) { reject_blob = c_outline_it.data()->flag(COUT_INVERSE) != blob_inverted; } if (reject_blob) { rej_cblob_it.add_after_then_move(start_it.extract()); } else { if (blob_inverted) inverted_vote++; else non_inverted_vote++; } } flags.set_bit(W_INVERSE, (inverted_vote > non_inverted_vote)); start_it.set_to_list(&cblobs); if (start_it.empty()) return; for (start_it.mark_cycle_pt(); !start_it.cycled_list(); start_it.forward()) { c_outline_it.set_to_list(start_it.data()->out_list()); if (c_outline_it.data()->flag(COUT_INVERSE) != flags.bit(W_INVERSE)) rej_cblob_it.add_after_then_move(start_it.extract()); } } /** * WERD::WERD * * Constructor to build a WERD from a list of C_BLOBs. * The C_BLOBs are not copied so the source list is emptied. */ WERD::WERD(C_BLOB_LIST * blob_list, //< In word order WERD * clone) //< Source of flags : flags(clone->flags), script_id_(clone->script_id_), correct(clone->correct) { C_BLOB_IT start_it = blob_list; // iterator C_BLOB_IT end_it = blob_list; // another while (!end_it.at_last ()) end_it.forward (); //move to last ((C_BLOB_LIST *) (&cblobs))->assign_to_sublist (&start_it, &end_it); //move to our list blanks = clone->blanks; // fprintf(stderr,"Wrong constructor!!!!\n"); } // Construct a WERD from a single_blob and clone the flags from this. // W_BOL and W_EOL flags are set according to the given values. WERD* WERD::ConstructFromSingleBlob(bool bol, bool eol, C_BLOB* blob) { C_BLOB_LIST temp_blobs; C_BLOB_IT temp_it(&temp_blobs); temp_it.add_after_then_move(blob); WERD* blob_word = new WERD(&temp_blobs, this); blob_word->set_flag(W_BOL, bol); blob_word->set_flag(W_EOL, eol); return blob_word; } /** * WERD::bounding_box * * Return the bounding box of the WERD. * This is quite a mess to compute! * ORIGINALLY, REJECT CBLOBS WERE EXCLUDED, however, this led to bugs when the * words on the row were re-sorted. The original words were built with reject * blobs included. The FUZZY SPACE flags were set accordingly. If ALL the * blobs in a word are rejected the BB for the word is NULL, causing the sort * to screw up, leading to the erroneous possibility of the first word in a * row being marked as FUZZY space. */ TBOX WERD::bounding_box() const { return restricted_bounding_box(true, true); } // Returns the bounding box including the desired combination of upper and // lower noise/diacritic elements. TBOX WERD::restricted_bounding_box(bool upper_dots, bool lower_dots) const { TBOX box = true_bounding_box(); int bottom = box.bottom(); int top = box.top(); // This is a read-only iteration of the rejected blobs. C_BLOB_IT it(const_cast(&rej_cblobs)); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { TBOX dot_box = it.data()->bounding_box(); if ((upper_dots || dot_box.bottom() <= top) && (lower_dots || dot_box.top() >= bottom)) { box += dot_box; } } return box; } // Returns the bounding box of only the good blobs. TBOX WERD::true_bounding_box() const { TBOX box; // box being built // This is a read-only iteration of the good blobs. C_BLOB_IT it(const_cast(&cblobs)); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { box += it.data()->bounding_box(); } return box; } /** * WERD::move * * Reposition WERD by vector * NOTE!! REJECT CBLOBS ARE NOT MOVED */ void WERD::move(const ICOORD vec) { C_BLOB_IT cblob_it(&cblobs); // cblob iterator for (cblob_it.mark_cycle_pt(); !cblob_it.cycled_list(); cblob_it.forward()) cblob_it.data()->move(vec); } /** * WERD::join_on * * Join other word onto this one. Delete the old word. */ void WERD::join_on(WERD* other) { C_BLOB_IT blob_it(&cblobs); C_BLOB_IT src_it(&other->cblobs); C_BLOB_IT rej_cblob_it(&rej_cblobs); C_BLOB_IT src_rej_it(&other->rej_cblobs); while (!src_it.empty()) { blob_it.add_to_end(src_it.extract()); src_it.forward(); } while (!src_rej_it.empty()) { rej_cblob_it.add_to_end(src_rej_it.extract()); src_rej_it.forward(); } } /** * WERD::copy_on * * Copy blobs from other word onto this one. */ void WERD::copy_on(WERD* other) { bool reversed = other->bounding_box().left() < bounding_box().left(); C_BLOB_IT c_blob_it(&cblobs); C_BLOB_LIST c_blobs; c_blobs.deep_copy(&other->cblobs, &C_BLOB::deep_copy); if (reversed) { c_blob_it.add_list_before(&c_blobs); } else { c_blob_it.move_to_last(); c_blob_it.add_list_after(&c_blobs); } if (!other->rej_cblobs.empty()) { C_BLOB_IT rej_c_blob_it(&rej_cblobs); C_BLOB_LIST new_rej_c_blobs; new_rej_c_blobs.deep_copy(&other->rej_cblobs, &C_BLOB::deep_copy); if (reversed) { rej_c_blob_it.add_list_before(&new_rej_c_blobs); } else { rej_c_blob_it.move_to_last(); rej_c_blob_it.add_list_after(&new_rej_c_blobs); } } } /** * WERD::print * * Display members */ void WERD::print() { tprintf("Blanks= %d\n", blanks); bounding_box().print(); tprintf("Flags = %d = 0%o\n", flags.val, flags.val); tprintf(" W_SEGMENTED = %s\n", flags.bit(W_SEGMENTED) ? "TRUE" : "FALSE "); tprintf(" W_ITALIC = %s\n", flags.bit(W_ITALIC) ? "TRUE" : "FALSE "); tprintf(" W_BOL = %s\n", flags.bit(W_BOL) ? "TRUE" : "FALSE "); tprintf(" W_EOL = %s\n", flags.bit(W_EOL) ? "TRUE" : "FALSE "); tprintf(" W_NORMALIZED = %s\n", flags.bit(W_NORMALIZED) ? "TRUE" : "FALSE "); tprintf(" W_SCRIPT_HAS_XHEIGHT = %s\n", flags.bit(W_SCRIPT_HAS_XHEIGHT) ? "TRUE" : "FALSE "); tprintf(" W_SCRIPT_IS_LATIN = %s\n", flags.bit(W_SCRIPT_IS_LATIN) ? "TRUE" : "FALSE "); tprintf(" W_DONT_CHOP = %s\n", flags.bit(W_DONT_CHOP) ? "TRUE" : "FALSE "); tprintf(" W_REP_CHAR = %s\n", flags.bit(W_REP_CHAR) ? "TRUE" : "FALSE "); tprintf(" W_FUZZY_SP = %s\n", flags.bit(W_FUZZY_SP) ? "TRUE" : "FALSE "); tprintf(" W_FUZZY_NON = %s\n", flags.bit(W_FUZZY_NON) ? "TRUE" : "FALSE "); tprintf("Correct= %s\n", correct.string()); tprintf("Rejected cblob count = %d\n", rej_cblobs.length()); tprintf("Script = %d\n", script_id_); } /** * WERD::plot * * Draw the WERD in the given colour. */ #ifndef GRAPHICS_DISABLED void WERD::plot(ScrollView *window, ScrollView::Color colour) { C_BLOB_IT it = &cblobs; for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { it.data()->plot(window, colour, colour); } plot_rej_blobs(window); } // Get the next color in the (looping) rainbow. ScrollView::Color WERD::NextColor(ScrollView::Color colour) { ScrollView::Color next = static_cast(colour + 1); if (next >= LAST_COLOUR || next < FIRST_COLOUR) next = FIRST_COLOUR; return next; } /** * WERD::plot * * Draw the WERD in rainbow colours in window. */ void WERD::plot(ScrollView* window) { ScrollView::Color colour = FIRST_COLOUR; C_BLOB_IT it = &cblobs; for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { it.data()->plot(window, colour, CHILD_COLOUR); colour = NextColor(colour); } plot_rej_blobs(window); } /** * WERD::plot_rej_blobs * * Draw the WERD rejected blobs in window - ALWAYS GREY */ void WERD::plot_rej_blobs(ScrollView *window) { C_BLOB_IT it = &rej_cblobs; for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { it.data()->plot(window, ScrollView::GREY, ScrollView::GREY); } } #endif // GRAPHICS_DISABLED /** * WERD::shallow_copy() * * Make a shallow copy of a word */ WERD *WERD::shallow_copy() { WERD *new_word = new WERD; new_word->blanks = blanks; new_word->flags = flags; new_word->dummy = dummy; new_word->correct = correct; return new_word; } /** * WERD::operator= * * Assign a word, DEEP copying the blob list */ WERD & WERD::operator= (const WERD & source) { this->ELIST2_LINK::operator= (source); blanks = source.blanks; flags = source.flags; script_id_ = source.script_id_; dummy = source.dummy; correct = source.correct; if (!cblobs.empty()) cblobs.clear(); cblobs.deep_copy(&source.cblobs, &C_BLOB::deep_copy); if (!rej_cblobs.empty()) rej_cblobs.clear(); rej_cblobs.deep_copy(&source.rej_cblobs, &C_BLOB::deep_copy); return *this; } /** * word_comparator() * * word comparator used to sort a word list so that words are in increasing * order of left edge. */ int word_comparator(const void *word1p, const void *word2p) { WERD *word1 = *(WERD **)word1p; WERD *word2 = *(WERD **)word2p; return word1->bounding_box().left() - word2->bounding_box().left(); } /** * WERD::ConstructWerdWithNewBlobs() * * This method returns a new werd constructed using the blobs in the input * all_blobs list, which correspond to the blobs in this werd object. The * blobs used to construct the new word are consumed and removed from the * input all_blobs list. * Returns NULL if the word couldn't be constructed. * Returns original blobs for which no matches were found in the output list * orphan_blobs (appends). */ WERD* WERD::ConstructWerdWithNewBlobs(C_BLOB_LIST* all_blobs, C_BLOB_LIST* orphan_blobs) { C_BLOB_LIST current_blob_list; C_BLOB_IT werd_blobs_it(¤t_blob_list); // Add the word's c_blobs. werd_blobs_it.add_list_after(cblob_list()); // New blob list. These contain the blobs which will form the new word. C_BLOB_LIST new_werd_blobs; C_BLOB_IT new_blobs_it(&new_werd_blobs); // not_found_blobs contains the list of current word's blobs for which a // corresponding blob wasn't found in the input all_blobs list. C_BLOB_LIST not_found_blobs; C_BLOB_IT not_found_it(¬_found_blobs); not_found_it.move_to_last(); werd_blobs_it.move_to_first(); for (werd_blobs_it.mark_cycle_pt(); !werd_blobs_it.cycled_list(); werd_blobs_it.forward()) { C_BLOB* werd_blob = werd_blobs_it.extract(); TBOX werd_blob_box = werd_blob->bounding_box(); bool found = false; // Now find the corresponding blob for this blob in the all_blobs // list. For now, follow the inefficient method of pairwise // comparisons. Ideally, one can pre-bucket the blobs by row. C_BLOB_IT all_blobs_it(all_blobs); for (all_blobs_it.mark_cycle_pt(); !all_blobs_it.cycled_list(); all_blobs_it.forward()) { C_BLOB* a_blob = all_blobs_it.data(); // Compute the overlap of the two blobs. If major, a_blob should // be added to the new blobs list. TBOX a_blob_box = a_blob->bounding_box(); if (a_blob_box.null_box()) { tprintf("Bounding box couldn't be ascertained\n"); } if (werd_blob_box.contains(a_blob_box) || werd_blob_box.major_overlap(a_blob_box)) { // Old blobs are from minimal splits, therefore are expected to be // bigger. The new small blobs should cover a significant portion. // This is it. all_blobs_it.extract(); new_blobs_it.add_after_then_move(a_blob); found = true; } } if (!found) { not_found_it.add_after_then_move(werd_blob); } else { delete werd_blob; } } // Iterate over all not found blobs. Some of them may be due to // under-segmentation (which is OK, since the corresponding blob is already // in the list in that case. not_found_it.move_to_first(); for (not_found_it.mark_cycle_pt(); !not_found_it.cycled_list(); not_found_it.forward()) { C_BLOB* not_found = not_found_it.data(); TBOX not_found_box = not_found->bounding_box(); C_BLOB_IT existing_blobs_it(new_blobs_it); for (existing_blobs_it.mark_cycle_pt(); !existing_blobs_it.cycled_list(); existing_blobs_it.forward()) { C_BLOB* a_blob = existing_blobs_it.data(); 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_fraction(a_blob_box) > 0.8) { // Already taken care of. delete not_found_it.extract(); break; } } } if (orphan_blobs) { C_BLOB_IT orphan_blobs_it(orphan_blobs); orphan_blobs_it.move_to_last(); orphan_blobs_it.add_list_after(¬_found_blobs); } // New blobs are ready. Create a new werd object with these. WERD* new_werd = NULL; if (!new_werd_blobs.empty()) { new_werd = new WERD(&new_werd_blobs, this); } else { // Add the blobs back to this word so that it can be reused. C_BLOB_IT this_list_it(cblob_list()); this_list_it.add_list_after(¬_found_blobs); } return new_werd; } // Removes noise from the word by moving small outlines to the rej_cblobs // list, based on the size_threshold. void WERD::CleanNoise(float size_threshold) { C_BLOB_IT blob_it(&cblobs); C_BLOB_IT rej_it(&rej_cblobs); for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { C_BLOB* blob = blob_it.data(); C_OUTLINE_IT ol_it(blob->out_list()); for (ol_it.mark_cycle_pt(); !ol_it.cycled_list(); ol_it.forward()) { C_OUTLINE* outline = ol_it.data(); TBOX ol_box = outline->bounding_box(); int ol_size = ol_box.width() > ol_box.height() ? ol_box.width() : ol_box.height(); if (ol_size < size_threshold) { // This outline is too small. Move it to a separate blob in the // reject blobs list. C_BLOB* rej_blob = new C_BLOB(ol_it.extract()); rej_it.add_after_then_move(rej_blob); } } if (blob->out_list()->empty()) delete blob_it.extract(); } } // Extracts all the noise outlines and stuffs the pointers into the given // vector of outlines. Afterwards, the outlines vector owns the pointers. void WERD::GetNoiseOutlines(GenericVector* outlines) { C_BLOB_IT rej_it(&rej_cblobs); for (rej_it.mark_cycle_pt(); !rej_it.empty(); rej_it.forward()) { C_BLOB* blob = rej_it.extract(); C_OUTLINE_IT ol_it(blob->out_list()); outlines->push_back(ol_it.extract()); delete blob; } } // Adds the selected outlines to the indcated real blobs, and puts the rest // back in rej_cblobs where they came from. Where the target_blobs entry is // NULL, a run of wanted outlines is put into a single new blob. // Ownership of the outlines is transferred back to the word. (Hence // GenericVector and not PointerVector.) // Returns true if any new blob was added to the start of the word, which // suggests that it might need joining to the word before it, and likewise // sets make_next_word_fuzzy true if any new blob was added to the end. bool WERD::AddSelectedOutlines(const GenericVector& wanted, const GenericVector& target_blobs, const GenericVector& outlines, bool* make_next_word_fuzzy) { bool outline_added_to_start = false; if (make_next_word_fuzzy != NULL) *make_next_word_fuzzy = false; C_BLOB_IT rej_it(&rej_cblobs); for (int i = 0; i < outlines.size(); ++i) { C_OUTLINE* outline = outlines[i]; if (outline == NULL) continue; // Already used it. if (wanted[i]) { C_BLOB* target_blob = target_blobs[i]; TBOX noise_box = outline->bounding_box(); if (target_blob == NULL) { target_blob = new C_BLOB(outline); // Need to find the insertion point. C_BLOB_IT blob_it(&cblobs); for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { C_BLOB* blob = blob_it.data(); TBOX blob_box = blob->bounding_box(); if (blob_box.left() > noise_box.left()) { if (blob_it.at_first() && !flag(W_FUZZY_SP) && !flag(W_FUZZY_NON)) { // We might want to join this word to its predecessor. outline_added_to_start = true; } blob_it.add_before_stay_put(target_blob); break; } } if (blob_it.cycled_list()) { blob_it.add_to_end(target_blob); if (make_next_word_fuzzy != NULL) *make_next_word_fuzzy = true; } // Add all consecutive wanted, but null-blob outlines to same blob. C_OUTLINE_IT ol_it(target_blob->out_list()); while (i + 1 < outlines.size() && wanted[i + 1] && target_blobs[i + 1] == NULL) { ++i; ol_it.add_to_end(outlines[i]); } } else { // Insert outline into this blob. C_OUTLINE_IT ol_it(target_blob->out_list()); ol_it.add_to_end(outline); } } else { // Put back on noise list. rej_it.add_to_end(new C_BLOB(outline)); } } return outline_added_to_start; } tesseract-3.04.01/ccstruct/werd.h000066400000000000000000000170301266071204500166240ustar00rootroot00000000000000/********************************************************************** * File: word.c * Description: Code for the WERD class. * Author: Ray Smith * Created: Tue Oct 08 14:32:12 BST 1991 * * (C) Copyright 1991, 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 WERD_H #define WERD_H #include "params.h" #include "bits16.h" #include "elst2.h" #include "strngs.h" #include "blckerr.h" #include "stepblob.h" enum WERD_FLAGS { W_SEGMENTED, //< correctly segmented W_ITALIC, //< italic text W_BOLD, //< bold text W_BOL, //< start of line W_EOL, //< end of line W_NORMALIZED, //< flags W_SCRIPT_HAS_XHEIGHT, //< x-height concept makes sense. W_SCRIPT_IS_LATIN, //< Special case latin for y. splitting. W_DONT_CHOP, //< fixed pitch chopped W_REP_CHAR, //< repeated character W_FUZZY_SP, //< fuzzy space W_FUZZY_NON, //< fuzzy nonspace W_INVERSE //< white on black }; enum DISPLAY_FLAGS { /* Display flags bit number allocations */ DF_BOX, //< Bounding box DF_TEXT, //< Correct ascii DF_POLYGONAL, //< Polyg approx DF_EDGE_STEP, //< Edge steps DF_BN_POLYGONAL, //< BL normalisd polyapx DF_BLAMER //< Blamer information }; class ROW; //forward decl class WERD : public ELIST2_LINK { public: WERD() {} // WERD constructed with: // blob_list - blobs of the word (we take this list's contents) // blanks - number of blanks before the word // text - correct text (outlives WERD) WERD(C_BLOB_LIST *blob_list, uinT8 blanks, const char *text); // WERD constructed from: // blob_list - blobs in the word // clone - werd to clone flags, etc from. WERD(C_BLOB_LIST *blob_list, WERD *clone); // Construct a WERD from a single_blob and clone the flags from this. // W_BOL and W_EOL flags are set according to the given values. WERD* ConstructFromSingleBlob(bool bol, bool eol, C_BLOB* blob); ~WERD() { } // assignment WERD & operator= (const WERD &source); // This method returns a new werd constructed using the blobs in the input // all_blobs list, which correspond to the blobs in this werd object. The // blobs used to construct the new word are consumed and removed from the // input all_blobs list. // Returns NULL if the word couldn't be constructed. // Returns original blobs for which no matches were found in the output list // orphan_blobs (appends). WERD *ConstructWerdWithNewBlobs(C_BLOB_LIST *all_blobs, C_BLOB_LIST *orphan_blobs); // Accessors for reject / DUFF blobs in various formats C_BLOB_LIST *rej_cblob_list() { // compact format return &rej_cblobs; } // Accessors for good blobs in various formats. C_BLOB_LIST *cblob_list() { // get compact blobs return &cblobs; } uinT8 space() { // access function return blanks; } void set_blanks(uinT8 new_blanks) { blanks = new_blanks; } int script_id() const { return script_id_; } void set_script_id(int id) { script_id_ = id; } // Returns the (default) bounding box including all the dots. TBOX bounding_box() const; // compute bounding box // Returns the bounding box including the desired combination of upper and // lower noise/diacritic elements. TBOX restricted_bounding_box(bool upper_dots, bool lower_dots) const; // Returns the bounding box of only the good blobs. TBOX true_bounding_box() const; const char *text() const { return correct.string(); } void set_text(const char *new_text) { correct = new_text; } BOOL8 flag(WERD_FLAGS mask) const { return flags.bit(mask); } void set_flag(WERD_FLAGS mask, BOOL8 value) { flags.set_bit(mask, value); } BOOL8 display_flag(uinT8 flag) const { return disp_flags.bit(flag); } void set_display_flag(uinT8 flag, BOOL8 value) { disp_flags.set_bit(flag, value); } WERD *shallow_copy(); // shallow copy word // reposition word by vector void move(const ICOORD vec); // join other's blobs onto this werd, emptying out other. void join_on(WERD* other); // copy other's blobs onto this word, leaving other intact. void copy_on(WERD* other); // tprintf word metadata (but not blob innards) void print(); #ifndef GRAPHICS_DISABLED // plot word on window in a uniform colour void plot(ScrollView *window, ScrollView::Color colour); // Get the next color in the (looping) rainbow. static ScrollView::Color NextColor(ScrollView::Color colour); // plot word on window in a rainbow of colours void plot(ScrollView *window); // plot rejected blobs in a rainbow of colours void plot_rej_blobs(ScrollView *window); #endif // GRAPHICS_DISABLED // Removes noise from the word by moving small outlines to the rej_cblobs // list, based on the size_threshold. void CleanNoise(float size_threshold); // Extracts all the noise outlines and stuffs the pointers into the given // vector of outlines. Afterwards, the outlines vector owns the pointers. void GetNoiseOutlines(GenericVector *outlines); // Adds the selected outlines to the indcated real blobs, and puts the rest // back in rej_cblobs where they came from. Where the target_blobs entry is // NULL, a run of wanted outlines is put into a single new blob. // Ownership of the outlines is transferred back to the word. (Hence // GenericVector and not PointerVector.) // Returns true if any new blob was added to the start of the word, which // suggests that it might need joining to the word before it, and likewise // sets make_next_word_fuzzy true if any new blob was added to the end. bool AddSelectedOutlines(const GenericVector &wanted, const GenericVector &target_blobs, const GenericVector &outlines, bool *make_next_word_fuzzy); private: uinT8 blanks; // no of blanks uinT8 dummy; // padding BITS16 flags; // flags about word BITS16 disp_flags; // display flags inT16 script_id_; // From unicharset. STRING correct; // correct text C_BLOB_LIST cblobs; // compacted blobs C_BLOB_LIST rej_cblobs; // DUFF blobs }; ELIST2IZEH (WERD) #include "ocrrow.h" // placed here due to // compare words by increasing order of left edge, suitable for qsort(3) int word_comparator(const void *word1p, const void *word2p); #endif tesseract-3.04.01/ccutil/000077500000000000000000000000001266071204500151425ustar00rootroot00000000000000tesseract-3.04.01/ccutil/Makefile.am000066400000000000000000000030611266071204500171760ustar00rootroot00000000000000AUTOMAKE_OPTIONS = subdir-objects SUBDIRS = AM_CXXFLAGS = if !NO_TESSDATA_PREFIX AM_CXXFLAGS += -DTESSDATA_PREFIX=@datadir@/ endif if VISIBILITY AM_CXXFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden AM_CPPFLAGS += -DTESS_EXPORTS endif 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 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 sorthelper.h stderr.h \ scanutils.h tessdatamanager.h tprintf.h unicity_table.h unicodes.h \ universalambigs.h if !USING_MULTIPLELIBS noinst_LTLIBRARIES = libtesseract_ccutil.la else lib_LTLIBRARIES = libtesseract_ccutil.la libtesseract_ccutil_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) endif libtesseract_ccutil_la_SOURCES = \ ambigs.cpp basedir.cpp bits16.cpp bitvector.cpp \ ccutil.cpp clst.cpp \ elst2.cpp elst.cpp errcode.cpp \ globaloc.cpp indexmapbidi.cpp \ mainblk.cpp memry.cpp \ serialis.cpp strngs.cpp scanutils.cpp \ tessdatamanager.cpp tprintf.cpp \ unichar.cpp unicharmap.cpp unicharset.cpp unicodes.cpp \ params.cpp universalambigs.cpp if T_WIN AM_CPPFLAGS += -I$(top_srcdir)/vs2010/port -DWINDLLNAME=\"lib@GENERIC_LIBRARY_NAME@\" noinst_HEADERS += ../vs2010/port/strtok_r.h libtesseract_ccutil_la_SOURCES += ../vs2010/port/strtok_r.cpp endif tesseract-3.04.01/ccutil/Makefile.in000066400000000000000000000764771266071204500172340ustar00rootroot00000000000000# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @NO_TESSDATA_PREFIX_FALSE@am__append_1 = -DTESSDATA_PREFIX=@datadir@/ @VISIBILITY_TRUE@am__append_2 = -fvisibility=hidden -fvisibility-inlines-hidden @VISIBILITY_TRUE@am__append_3 = -DTESS_EXPORTS @T_WIN_TRUE@am__append_4 = -I$(top_srcdir)/vs2010/port -DWINDLLNAME=\"lib@GENERIC_LIBRARY_NAME@\" @T_WIN_TRUE@am__append_5 = ../vs2010/port/strtok_r.h @T_WIN_TRUE@am__append_6 = ../vs2010/port/strtok_r.cpp subdir = ccutil DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/config/depcomp $(include_HEADERS) \ $(am__noinst_HEADERS_DIST) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config_auto.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)" LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) libtesseract_ccutil_la_LIBADD = am__libtesseract_ccutil_la_SOURCES_DIST = ambigs.cpp basedir.cpp \ bits16.cpp bitvector.cpp ccutil.cpp clst.cpp elst2.cpp \ elst.cpp errcode.cpp globaloc.cpp indexmapbidi.cpp mainblk.cpp \ memry.cpp serialis.cpp strngs.cpp scanutils.cpp \ tessdatamanager.cpp tprintf.cpp unichar.cpp unicharmap.cpp \ unicharset.cpp unicodes.cpp params.cpp universalambigs.cpp \ ../vs2010/port/strtok_r.cpp am__dirstamp = $(am__leading_dot)dirstamp @T_WIN_TRUE@am__objects_1 = ../vs2010/port/strtok_r.lo am_libtesseract_ccutil_la_OBJECTS = ambigs.lo basedir.lo bits16.lo \ bitvector.lo ccutil.lo clst.lo elst2.lo elst.lo errcode.lo \ globaloc.lo indexmapbidi.lo mainblk.lo memry.lo serialis.lo \ strngs.lo scanutils.lo tessdatamanager.lo tprintf.lo \ unichar.lo unicharmap.lo unicharset.lo unicodes.lo params.lo \ universalambigs.lo $(am__objects_1) libtesseract_ccutil_la_OBJECTS = $(am_libtesseract_ccutil_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libtesseract_ccutil_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ $(AM_CXXFLAGS) $(CXXFLAGS) $(libtesseract_ccutil_la_LDFLAGS) \ $(LDFLAGS) -o $@ @USING_MULTIPLELIBS_FALSE@am_libtesseract_ccutil_la_rpath = @USING_MULTIPLELIBS_TRUE@am_libtesseract_ccutil_la_rpath = -rpath \ @USING_MULTIPLELIBS_TRUE@ $(libdir) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__depfiles_maybe = depfiles am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(libtesseract_ccutil_la_SOURCES) DIST_SOURCES = $(am__libtesseract_ccutil_la_SOURCES_DIST) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__noinst_HEADERS_DIST = 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 sorthelper.h stderr.h scanutils.h \ tessdatamanager.h tprintf.h unicity_table.h unicodes.h \ universalambigs.h ../vs2010/port/strtok_r.h HEADERS = $(include_HEADERS) $(noinst_HEADERS) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_CPPFLAGS = @AM_CPPFLAGS@ $(am__append_3) $(am__append_4) AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AM_LDFLAGS = @AM_LDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FRAMEWORK_OPENCL = @FRAMEWORK_OPENCL@ GENERIC_API_VERSION = @GENERIC_API_VERSION@ GENERIC_LIBRARY_NAME = @GENERIC_LIBRARY_NAME@ GENERIC_LIBRARY_VERSION = @GENERIC_LIBRARY_VERSION@ GENERIC_MAJOR_VERSION = @GENERIC_MAJOR_VERSION@ GENERIC_RELEASE = @GENERIC_RELEASE@ GENERIC_VERSION = @GENERIC_VERSION@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBLEPT_HEADERSDIR = @LIBLEPT_HEADERSDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCL_CPPFLAGS = @OPENCL_CPPFLAGS@ OPENCL_LDFLAGS = @OPENCL_LDFLAGS@ OPENMP_CXXFLAGS = @OPENMP_CXXFLAGS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_DATE = @PACKAGE_DATE@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_YEAR = @PACKAGE_YEAR@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = subdir-objects SUBDIRS = AM_CXXFLAGS = $(am__append_1) $(am__append_2) 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 \ 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 sorthelper.h stderr.h scanutils.h \ tessdatamanager.h tprintf.h unicity_table.h unicodes.h \ universalambigs.h $(am__append_5) @USING_MULTIPLELIBS_FALSE@noinst_LTLIBRARIES = libtesseract_ccutil.la @USING_MULTIPLELIBS_TRUE@lib_LTLIBRARIES = libtesseract_ccutil.la @USING_MULTIPLELIBS_TRUE@libtesseract_ccutil_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) libtesseract_ccutil_la_SOURCES = ambigs.cpp basedir.cpp bits16.cpp \ bitvector.cpp ccutil.cpp clst.cpp elst2.cpp elst.cpp \ errcode.cpp globaloc.cpp indexmapbidi.cpp mainblk.cpp \ memry.cpp serialis.cpp strngs.cpp scanutils.cpp \ tessdatamanager.cpp tprintf.cpp unichar.cpp unicharmap.cpp \ unicharset.cpp unicodes.cpp params.cpp universalambigs.cpp \ $(am__append_6) all: all-recursive .SUFFIXES: .SUFFIXES: .cpp .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign ccutil/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign ccutil/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } ../vs2010/port/$(am__dirstamp): @$(MKDIR_P) ../vs2010/port @: > ../vs2010/port/$(am__dirstamp) ../vs2010/port/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ../vs2010/port/$(DEPDIR) @: > ../vs2010/port/$(DEPDIR)/$(am__dirstamp) ../vs2010/port/strtok_r.lo: ../vs2010/port/$(am__dirstamp) \ ../vs2010/port/$(DEPDIR)/$(am__dirstamp) libtesseract_ccutil.la: $(libtesseract_ccutil_la_OBJECTS) $(libtesseract_ccutil_la_DEPENDENCIES) $(EXTRA_libtesseract_ccutil_la_DEPENDENCIES) $(AM_V_CXXLD)$(libtesseract_ccutil_la_LINK) $(am_libtesseract_ccutil_la_rpath) $(libtesseract_ccutil_la_OBJECTS) $(libtesseract_ccutil_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f ../vs2010/port/*.$(OBJEXT) -rm -f ../vs2010/port/*.lo distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@../vs2010/port/$(DEPDIR)/strtok_r.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ambigs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/basedir.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bits16.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bitvector.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ccutil.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clst.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elst.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elst2.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errcode.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/globaloc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/indexmapbidi.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mainblk.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memry.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/params.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scanutils.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/serialis.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strngs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tessdatamanager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tprintf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unichar.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unicharmap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unicharset.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unicodes.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/universalambigs.Plo@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs -rm -rf ../vs2010/port/.libs ../vs2010/port/_libs install-includeHEADERS: $(include_HEADERS) @$(NORMAL_INSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ done uninstall-includeHEADERS: @$(NORMAL_UNINSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f ../vs2010/port/$(DEPDIR)/$(am__dirstamp) -rm -f ../vs2010/port/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ clean-noinstLTLIBRARIES mostlyclean-am distclean: distclean-recursive -rm -rf ../vs2010/port/$(DEPDIR) ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-includeHEADERS install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-libLTLIBRARIES install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -rf ../vs2010/port/$(DEPDIR) ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libLTLIBRARIES \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am \ install-includeHEADERS install-info install-info-am \ install-libLTLIBRARIES install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-includeHEADERS \ uninstall-libLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: tesseract-3.04.01/ccutil/ambigs.cpp000066400000000000000000000365621266071204500171240ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: ambigs.cc // Description: Functions for dealing with ambiguities // (training and recognition). // Author: Daria Antonova // Created: Mon Feb 5 11:26:43 PDT 2009 // // (C) Copyright 2008, 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 "ambigs.h" #include #include "helpers.h" #include "universalambigs.h" #if defined _WIN32 || defined(__CYGWIN__) #ifndef __GNUC__ #define strtok_r strtok_s #else #include "strtok_r.h" #endif /* __GNUC__ */ #endif /* _WIN32 __CYGWIN__*/ 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; correct_ngram_id = INVALID_UNICHAR_ID; type = NOT_AMBIG; wrong_ngram_size = 0; } ELISTIZE(AmbigSpec); // 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); if (use_ambigs_for_adaption) { ambigs_for_adaption_.push_back(NULL); 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) { TFile file; if (!file.Open(kUniversalAmbigsFile, ksizeofUniversalAmbigsFile)) return; LoadUnicharAmbigs(encoder_set, &file, 0, false, unicharset); } void UnicharAmbigs::LoadUnicharAmbigs(const UNICHARSET& encoder_set, TFile *ambig_file, 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 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 kBufferSize = 10 + 2 * kMaxAmbigStringSize; char *buffer = new char[kBufferSize]; 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(ambig_file->FGets(buffer, kBufferSize) != NULL && strlen(buffer) > 0); if (*buffer == 'v') { version = static_cast(strtol(buffer+1, NULL, 10)); ++line_num; } else { ambig_file->Rewind(); } while (ambig_file->FGets(buffer, kBufferSize) != NULL) { chomp_string(buffer); if (debug_level > 2) tprintf("read line %s\n", buffer); ++line_num; 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(); 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 (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_[test_unichar_ids[0]]->push_back( ambig_spec->correct_ngram_id); } // Update ambigs_for_adaption_. if (use_ambigs_for_adaption) { 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); } } } } } } delete[] buffer; // Fill in reverse_ambigs_for_adaption from ambigs_for_adaption vector. if (use_ambigs_for_adaption) { for (i = 0; i < ambigs_for_adaption_.size(); ++i) { adaption_ambigs_entry = ambigs_for_adaption_[i]; if (adaption_ambigs_entry == NULL) continue; for (j = 0; j < adaption_ambigs_entry->size(); ++j) { UNICHAR_ID ambig_id = (*adaption_ambigs_entry)[j]; if (reverse_ambigs_for_adaption_[ambig_id] == NULL) { reverse_ambigs_for_adaption_[ambig_id] = new UnicharIdVector(); } reverse_ambigs_for_adaption_[ambig_id]->push_back(i); } } } // Print what was read from the input file. if (debug_level > 1) { for (int tbl = 0; tbl < 2; ++tbl) { const UnicharAmbigsVector &print_table = (tbl == 0) ? replace_ambigs_ : dang_ambigs_; for (i = 0; i < print_table.size(); ++i) { AmbigSpec_LIST *lst = print_table[i]; if (lst == NULL) continue; if (!lst->empty()) { tprintf("%s Ambiguities for %s:\n", (tbl == 0) ? "Replaceable" : "Dangerous", unicharset->debug_str(i).string()); } AmbigSpec_IT lst_it(lst); for (lst_it.mark_cycle_pt(); !lst_it.cycled_list(); lst_it.forward()) { AmbigSpec *ambig_spec = lst_it.data(); tprintf("wrong_ngram:"); UnicharIdArrayUtils::print(ambig_spec->wrong_ngram, *unicharset); tprintf("correct_fragments:"); UnicharIdArrayUtils::print(ambig_spec->correct_fragments, *unicharset); } } } if (use_ambigs_for_adaption) { for (int vec_id = 0; vec_id < 2; ++vec_id) { const GenericVector &vec = (vec_id == 0) ? ambigs_for_adaption_ : reverse_ambigs_for_adaption_; for (i = 0; i < vec.size(); ++i) { adaption_ambigs_entry = vec[i]; if (adaption_ambigs_entry != NULL) { tprintf("%sAmbigs for adaption for %s:\n", (vec_id == 0) ? "" : "Reverse ", unicharset->debug_str(i).string()); for (j = 0; j < adaption_ambigs_entry->size(); ++j) { tprintf("%s ", unicharset->debug_str( (*adaption_ambigs_entry)[j]).string()); } tprintf("\n"); } } } } } } bool UnicharAmbigs::ParseAmbiguityLine( int line_num, int version, int debug_level, const UNICHARSET &unicharset, 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", test_ambig_part_size) || *test_ambig_part_size <= 0) { if (debug_level) tprintf(kIllegalMsg, line_num); return false; } 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 < *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; } test_unichar_ids[i] = unicharset.unichar_to_id(token); } test_unichar_ids[i] = INVALID_UNICHAR_ID; if (i != *test_ambig_part_size || !(token = strtok_r(NULL, kAmbigDelimiters, &next_token)) || !sscanf(token, "%d", replacement_ambig_part_size) || *replacement_ambig_part_size <= 0) { if (debug_level) tprintf(kIllegalMsg, line_num); return false; } 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; } replacement_string[0] = '\0'; for (i = 0; i < *replacement_ambig_part_size; ++i) { if (!(token = strtok_r(NULL, kAmbigDelimiters, &next_token))) break; strcat(replacement_string, token); if (!unicharset.contains_unichar(token)) { if (debug_level) tprintf(kIllegalUnicharMsg, token); break; } } if (i != *replacement_ambig_part_size) { if (debug_level) tprintf(kIllegalMsg, line_num); return false; } if (version > 0) { // The next field being true indicates that the abiguity should // always be substituted (e.g. '' should always be changed to "). // For such "certain" n -> m ambigs tesseract will insert character // fragments for the n pieces in the unicharset. AmbigsFound() // will then replace the incorrect ngram with the character // fragments of the correct character (or ngram if m > 1). // Note that if m > 1, an ngram will be inserted into the // modified word, not the individual unigrams. Tesseract // has limited support for ngram unichar (e.g. dawg permuter). if (!(token = strtok_r(NULL, kAmbigDelimiters, &next_token)) || !sscanf(token, "%d", type)) { if (debug_level) tprintf(kIllegalMsg, line_num); return false; } } return true; } 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 (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(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 // each n->m ambiguity we will have to place n character fragments of the // correct ngram into the corresponding positions in the vector (e.g. given // "vvvvw" and vvvv->ww we will place v and |ww|0|4 into position 0, v and // |ww|1|4 into position 1 and so on. The correct ngram is reconstructed // from fragments by dawg_permute_and_select(). // 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(replacement_string); ambig_spec->correct_ngram_id = 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 < test_ambig_part_size; ++i) { UNICHAR_ID unichar_id; if (test_ambig_part_size == 1) { unichar_id = ambig_spec->correct_ngram_id; } else { STRING frag_str = CHAR_FRAGMENT::to_string( replacement_string, i, test_ambig_part_size, false); unicharset->unichar_insert(frag_str.string()); unichar_id = unicharset->unichar_to_id(frag_str.string()); } ambig_spec->correct_fragments[i] = unichar_id; } ambig_spec->correct_fragments[i] = INVALID_UNICHAR_ID; // Add AmbigSpec for this ambiguity to the corresponding AmbigSpec_LIST. // Keep AmbigSpec_LISTs sorted by AmbigSpec.wrong_ngram. if (table[test_unichar_ids[0]] == NULL) { table[test_unichar_ids[0]] = new AmbigSpec_LIST(); } if (table[test_unichar_ids[0]]->add_sorted( AmbigSpec::compare_ambig_specs, true, ambig_spec)) return true; delete ambig_spec; return false; } } // namespace tesseract tesseract-3.04.01/ccutil/ambigs.h000066400000000000000000000223531266071204500165620ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: ambigs.h // Description: Constants, flags, functions for dealing with // ambiguities (training and recognition). // Author: Daria Antonova // Created: Mon Aug 23 11:26:43 PDT 2008 // // (C) Copyright 2008, 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_AMBIGS_H_ #define TESSERACT_CCUTIL_AMBIGS_H_ #include "elst.h" #include "tprintf.h" #include "unichar.h" #include "unicharset.h" #include "genericvector.h" #define MAX_AMBIG_SIZE 10 namespace tesseract { typedef GenericVector UnicharIdVector; static const int kUnigramAmbigsBufferSize = 1000; static const char kAmbigNgramSeparator[] = { ' ', '\0' }; static const char kAmbigDelimiters[] = "\t "; static const char kIllegalMsg[] = "Illegal ambiguity specification on line %d\n"; static const char kIllegalUnicharMsg[] = "Illegal unichar %s in ambiguity specification\n"; enum AmbigType { NOT_AMBIG, // the ngram pair is not ambiguous REPLACE_AMBIG, // ocred ngram should always be substituted with correct DEFINITE_AMBIG, // add correct ngram to the classifier results (1-1) SIMILAR_AMBIG, // use pairwise classifier for ocred/correct pair (1-1) CASE_AMBIG, // this is a case ambiguity (1-1) AMBIG_TYPE_COUNT // number of enum entries }; // A collection of utility functions for arrays of UNICHAR_IDs that are // terminated by INVALID_UNICHAR_ID. class UnicharIdArrayUtils { public: // Compares two arrays of unichar ids. Returns -1 if the length of array1 is // less than length of array2, if any array1[i] is less than array2[i]. // Returns 0 if the arrays are equal, 1 otherwise. // The function assumes that the arrays are terminated by INVALID_UNICHAR_ID. static inline int compare(const UNICHAR_ID array1[], const UNICHAR_ID array2[]) { const UNICHAR_ID *ptr1 = array1; const UNICHAR_ID *ptr2 = array2; while (*ptr1 != INVALID_UNICHAR_ID && *ptr2 != INVALID_UNICHAR_ID) { if (*ptr1 != *ptr2) return *ptr1 < *ptr2 ? -1 : 1; ++ptr1; ++ptr2; } if (*ptr1 == INVALID_UNICHAR_ID && *ptr2 == INVALID_UNICHAR_ID) return 0; return *ptr1 == INVALID_UNICHAR_ID ? -1 : 1; } // Look uid in the vector of uids. If found, the index of the matched // element is returned. Otherwise, it returns -1. static inline int find_in(const UnicharIdVector& uid_vec, const UNICHAR_ID uid) { for (int i = 0; i < uid_vec.size(); ++i) if (uid_vec[i] == uid) return i; return -1; } // Copies UNICHAR_IDs from dst to src. Returns the number of ids copied. // The function assumes that the arrays are terminated by INVALID_UNICHAR_ID // and that dst has enough space for all the elements from src. static inline int copy(const UNICHAR_ID src[], UNICHAR_ID dst[]) { int i = 0; do { dst[i] = src[i]; } while (dst[i++] != INVALID_UNICHAR_ID); return i - 1; } // Prints unichars corresponding to the unichar_ids in the given array. // The function assumes that array is terminated by INVALID_UNICHAR_ID. static inline void print(const UNICHAR_ID array[], const UNICHARSET &unicharset) { const UNICHAR_ID *ptr = array; if (*ptr == INVALID_UNICHAR_ID) tprintf("[Empty]"); while (*ptr != INVALID_UNICHAR_ID) { tprintf("%s ", unicharset.id_to_unichar(*ptr++)); } tprintf("( "); ptr = array; while (*ptr != INVALID_UNICHAR_ID) tprintf("%d ", *ptr++); tprintf(")\n"); } }; // AMBIG_SPEC_LIST stores a list of dangerous ambigs that // start with the same unichar (e.g. r->t rn->m rr1->m). class AmbigSpec : public ELIST_LINK { public: AmbigSpec(); ~AmbigSpec() {} // Comparator function for sorting AmbigSpec_LISTs. The lists will // be sorted by their wrong_ngram arrays. Example of wrong_ngram vectors // in a a sorted AmbigSpec_LIST: [9 1 3], [9 3 4], [9 8], [9, 8 1]. static int compare_ambig_specs(const void *spec1, const void *spec2) { const AmbigSpec *s1 = *reinterpret_cast(spec1); const AmbigSpec *s2 = *reinterpret_cast(spec2); 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]; UNICHAR_ID correct_fragments[MAX_AMBIG_SIZE + 1]; UNICHAR_ID correct_ngram_id; AmbigType type; int wrong_ngram_size; }; ELISTIZEH(AmbigSpec); // AMBIG_TABLE[i] stores a set of ambiguities whose // wrong ngram starts with unichar id i. typedef GenericVector UnicharAmbigsVector; class UnicharAmbigs { public: UnicharAmbigs() {} ~UnicharAmbigs() { replace_ambigs_.delete_data_pointers(); dang_ambigs_.delete_data_pointers(); one_to_one_definite_ambigs_.delete_data_pointers(); } 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 // a list of potential ambiguities which can start with the corresponding // character. For example the ambiguity "rn -> m", would be located in the // table at index of unicharset.unichar_to_id('r'). // In 1-1 ambiguities (e.g. s -> S, 1 -> I) are recorded in // 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. // 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, TFile *ambigs_file, int debug_level, bool use_ambigs_for_adaption, UNICHARSET *unicharset); // Returns definite 1-1 ambigs for the given unichar id. inline const UnicharIdVector *OneToOneDefiniteAmbigs( UNICHAR_ID unichar_id) const { if (one_to_one_definite_ambigs_.empty()) return NULL; return one_to_one_definite_ambigs_[unichar_id]; } // Returns a pointer to the vector with all unichar ids that appear in the // 'correct' part of the ambiguity pair when the given unichar id appears // in the 'wrong' part of the ambiguity. E.g. if DangAmbigs file consist of // m->rn,rn->m,m->iii, UnicharAmbigsForAdaption() called with unichar id of // m will return a pointer to a vector with unichar ids of r,n,i. inline const UnicharIdVector *AmbigsForAdaption( UNICHAR_ID unichar_id) const { if (ambigs_for_adaption_.empty()) return NULL; return ambigs_for_adaption_[unichar_id]; } // Similar to the above, but return the vector of unichar ids for which // the given unichar_id is an ambiguity (appears in the 'wrong' part of // some ambiguity pair). inline const UnicharIdVector *ReverseAmbigsForAdaption( UNICHAR_ID unichar_id) const { if (reverse_ambigs_for_adaption_.empty()) return NULL; return reverse_ambigs_for_adaption_[unichar_id]; } private: bool ParseAmbiguityLine(int line_num, int version, int debug_level, const UNICHARSET &unicharset, char *buffer, 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_; GenericVector ambigs_for_adaption_; GenericVector reverse_ambigs_for_adaption_; }; } // namespace tesseract #endif // TESSERACT_CCUTIL_AMBIGS_H_ tesseract-3.04.01/ccutil/basedir.cpp000066400000000000000000000036701266071204500172650ustar00rootroot00000000000000/********************************************************************** * File: basedir.c (Formerly getpath.c) * Description: Find the directory location of the current executable using PATH. * Author: Ray Smith * Created: Mon Jul 09 09:06:39 BST 1990 * * (C) Copyright 1990, 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. * **********************************************************************/ #include "basedir.h" #include // Assuming that code_path is the name of some file in a desired directory, // returns the given code_path stripped back to the last slash, leaving // the last slash in place. If there is no slash, returns ./ assuming that // the input was the name of something in the current directory. // Useful for getting to the directory of argv[0], but does not search // any paths. TESS_API void truncate_path(const char *code_path, STRING* trunc_path) { int trunc_index = -1; if (code_path != NULL) { const char* last_slash = strrchr(code_path, '/'); if (last_slash != NULL && last_slash + 1 - code_path > trunc_index) trunc_index = last_slash + 1 - code_path; last_slash = strrchr(code_path, '\\'); if (last_slash != NULL && last_slash + 1 - code_path > trunc_index) trunc_index = last_slash + 1 - code_path; } *trunc_path = code_path; if (trunc_index >= 0) trunc_path->truncate_at(trunc_index); else *trunc_path = "./"; } tesseract-3.04.01/ccutil/basedir.h000066400000000000000000000024101266071204500167210ustar00rootroot00000000000000/********************************************************************** * File: basedir.h (Formerly getpath.h) * Description: Header file for getpath.c. Provides relocatability of data. * Author: Ray Smith * Created: Mon Jul 09 09:13:03 BST 1990 * * (C) Copyright 1990, 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 BASEDIR_H #define BASEDIR_H #include "platform.h" #include "strngs.h" // Returns the given code_path truncated to the last slash. // Useful for getting to the directory of argv[0], but does not search // any paths. TESS_API void truncate_path(const char *code_path, STRING* trunc_path); #endif tesseract-3.04.01/ccutil/bits16.cpp000066400000000000000000000023731266071204500167630ustar00rootroot00000000000000/********************************************************************** * File: bits16.h (Formerly bits8.h) * Description: Code for 8 bit field class. * Author: Phil Cheatle * Created: Thu Oct 17 10:10:05 BST 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include "bits16.h" /********************************************************************** * Constructor. Something to get it past the compiler as almost all inlined. * **********************************************************************/ BITS16::BITS16( // constructor uinT16 init) { // initial val val = init; } tesseract-3.04.01/ccutil/bits16.h000066400000000000000000000036421266071204500164300ustar00rootroot00000000000000/********************************************************************** * File: bits16.h (Formerly bits8.h) * Description: Code for 8 bit field class. * Author: Phil Cheatle * Created: Thu Oct 17 10:10:05 BST 1991 * * (C) Copyright 1991, 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 BITS16_H #define BITS16_H #include "host.h" class DLLSYM BITS16 { public: uinT16 val; BITS16() { val = 0; } // constructor BITS16( // constructor uinT16 init); // initial val void turn_on_bit( // flip specified bit uinT8 bit_num) { // bit to flip 0..7 val = val | 01 << bit_num; }; void turn_off_bit( // flip specified bit uinT8 bit_num) { // bit to flip 0..7 val = val & ~(01 << bit_num); }; void set_bit( // flip specified bit uinT8 bit_num, // bit to flip 0..7 BOOL8 value) { // value to flip to if (value) val = val | 01 << bit_num; else val = val & ~(01 << bit_num); }; BOOL8 bit( // access bit uinT8 bit_num) const { // bit to access return (val >> bit_num) & 01; }; }; #endif tesseract-3.04.01/ccutil/bitvector.cpp000066400000000000000000000227171266071204500176600ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: bitvector.cpp // Description: Class replacement for BITVECTOR. // Author: Ray Smith // Created: Mon Jan 10 17:45:01 PST 2011 // // (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. // /////////////////////////////////////////////////////////////////////// #include "bitvector.h" #include #include "helpers.h" #include "ndminx.h" namespace tesseract { // Fast lookup table to get the first least significant set bit in a byte. // For zero, the table has 255, but since it is a special case, most code // that uses this table will check for zero before looking up lsb_index_. const uinT8 BitVector::lsb_index_[256] = { 255, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; // Fast lookup table to get the residual bits after zeroing the first (lowest) // set bit in a byte. const uinT8 BitVector::lsb_eroded_[256] = { 0, 0, 0, 0x2, 0, 0x4, 0x4, 0x6, 0, 0x8, 0x8, 0x0a, 0x08, 0x0c, 0x0c, 0x0e, 0, 0x10, 0x10, 0x12, 0x10, 0x14, 0x14, 0x16, 0x10, 0x18, 0x18, 0x1a, 0x18, 0x1c, 0x1c, 0x1e, 0, 0x20, 0x20, 0x22, 0x20, 0x24, 0x24, 0x26, 0x20, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x2c, 0x2e, 0x20, 0x30, 0x30, 0x32, 0x30, 0x34, 0x34, 0x36, 0x30, 0x38, 0x38, 0x3a, 0x38, 0x3c, 0x3c, 0x3e, 0, 0x40, 0x40, 0x42, 0x40, 0x44, 0x44, 0x46, 0x40, 0x48, 0x48, 0x4a, 0x48, 0x4c, 0x4c, 0x4e, 0x40, 0x50, 0x50, 0x52, 0x50, 0x54, 0x54, 0x56, 0x50, 0x58, 0x58, 0x5a, 0x58, 0x5c, 0x5c, 0x5e, 0x40, 0x60, 0x60, 0x62, 0x60, 0x64, 0x64, 0x66, 0x60, 0x68, 0x68, 0x6a, 0x68, 0x6c, 0x6c, 0x6e, 0x60, 0x70, 0x70, 0x72, 0x70, 0x74, 0x74, 0x76, 0x70, 0x78, 0x78, 0x7a, 0x78, 0x7c, 0x7c, 0x7e, 0, 0x80, 0x80, 0x82, 0x80, 0x84, 0x84, 0x86, 0x80, 0x88, 0x88, 0x8a, 0x88, 0x8c, 0x8c, 0x8e, 0x80, 0x90, 0x90, 0x92, 0x90, 0x94, 0x94, 0x96, 0x90, 0x98, 0x98, 0x9a, 0x98, 0x9c, 0x9c, 0x9e, 0x80, 0xa0, 0xa0, 0xa2, 0xa0, 0xa4, 0xa4, 0xa6, 0xa0, 0xa8, 0xa8, 0xaa, 0xa8, 0xac, 0xac, 0xae, 0xa0, 0xb0, 0xb0, 0xb2, 0xb0, 0xb4, 0xb4, 0xb6, 0xb0, 0xb8, 0xb8, 0xba, 0xb8, 0xbc, 0xbc, 0xbe, 0x80, 0xc0, 0xc0, 0xc2, 0xc0, 0xc4, 0xc4, 0xc6, 0xc0, 0xc8, 0xc8, 0xca, 0xc8, 0xcc, 0xcc, 0xce, 0xc0, 0xd0, 0xd0, 0xd2, 0xd0, 0xd4, 0xd4, 0xd6, 0xd0, 0xd8, 0xd8, 0xda, 0xd8, 0xdc, 0xdc, 0xde, 0xc0, 0xe0, 0xe0, 0xe2, 0xe0, 0xe4, 0xe4, 0xe6, 0xe0, 0xe8, 0xe8, 0xea, 0xe8, 0xec, 0xec, 0xee, 0xe0, 0xf0, 0xf0, 0xf2, 0xf0, 0xf4, 0xf4, 0xf6, 0xf0, 0xf8, 0xf8, 0xfa, 0xf8, 0xfc, 0xfc, 0xfe }; // Fast lookup table to give the number of set bits in a byte. const int BitVector::hamming_table_[256] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 }; BitVector::BitVector() : bit_size_(0), array_(NULL) {} BitVector::BitVector(int length) : bit_size_(length) { array_ = new uinT32[WordLength()]; SetAllFalse(); } BitVector::BitVector(const BitVector& src) : bit_size_(src.bit_size_) { array_ = new uinT32[WordLength()]; memcpy(array_, src.array_, ByteLength()); } BitVector& BitVector::operator=(const BitVector& src) { Alloc(src.bit_size_); memcpy(array_, src.array_, ByteLength()); return *this; } BitVector::~BitVector() { delete [] array_; } // Initializes the array to length * false. void BitVector::Init(int length) { Alloc(length); SetAllFalse(); } // Writes to the given file. Returns false in case of error. bool BitVector::Serialize(FILE* fp) const { if (fwrite(&bit_size_, sizeof(bit_size_), 1, fp) != 1) return false; int wordlen = WordLength(); if (static_cast(fwrite(array_, sizeof(*array_), wordlen, fp)) != wordlen) return false; return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool BitVector::DeSerialize(bool swap, FILE* fp) { uinT32 new_bit_size; if (fread(&new_bit_size, sizeof(new_bit_size), 1, fp) != 1) return false; if (swap) { ReverseN(&new_bit_size, sizeof(new_bit_size)); } Alloc(new_bit_size); int wordlen = WordLength(); if (static_cast(fread(array_, sizeof(*array_), wordlen, fp)) != wordlen) return false; if (swap) { for (int i = 0; i < wordlen; ++i) ReverseN(&array_[i], sizeof(array_[i])); } return true; } void BitVector::SetAllFalse() { memset(array_, 0, ByteLength()); } void BitVector::SetAllTrue() { memset(array_, ~0, ByteLength()); } // Returns the index of the next set bit after the given index. // Useful for quickly iterating through the set bits in a sparse vector. int BitVector::NextSetBit(int prev_bit) const { // Move on to the next bit. int next_bit = prev_bit + 1; if (next_bit >= bit_size_) return -1; // Check the remains of the word containing the next_bit first. int next_word = WordIndex(next_bit); int bit_index = next_word * kBitFactor; int word_end = bit_index + kBitFactor; uinT32 word = array_[next_word]; uinT8 byte = word & 0xff; while (bit_index < word_end) { if (bit_index + 8 > next_bit && byte != 0) { while (bit_index + lsb_index_[byte] < next_bit && byte != 0) byte = lsb_eroded_[byte]; if (byte != 0) return bit_index + lsb_index_[byte]; } word >>= 8; bit_index += 8; byte = word & 0xff; } // next_word didn't contain a 1, so find the next word with set bit. ++next_word; int wordlen = WordLength(); while (next_word < wordlen && (word = array_[next_word]) == 0) { ++next_word; bit_index += kBitFactor; } if (bit_index >= bit_size_) return -1; // Find the first non-zero byte within the word. while ((word & 0xff) == 0) { word >>= 8; bit_index += 8; } return bit_index + lsb_index_[word & 0xff]; } // Returns the number of set bits in the vector. int BitVector::NumSetBits() const { int wordlen = WordLength(); int total_bits = 0; for (int w = 0; w < wordlen; ++w) { uinT32 word = array_[w]; for (int i = 0; i < 4; ++i) { total_bits += hamming_table_[word & 0xff]; word >>= 8; } } return total_bits; } // Logical in-place operations on whole bit vectors. Tries to do something // sensible if they aren't the same size, but they should be really. void BitVector::operator|=(const BitVector& other) { int length = MIN(WordLength(), other.WordLength()); for (int w = 0; w < length; ++w) array_[w] |= other.array_[w]; } void BitVector::operator&=(const BitVector& other) { int length = MIN(WordLength(), other.WordLength()); for (int w = 0; w < length; ++w) array_[w] &= other.array_[w]; for (int w = WordLength() - 1; w >= length; --w) array_[w] = 0; } void BitVector::operator^=(const BitVector& other) { int length = MIN(WordLength(), other.WordLength()); for (int w = 0; w < length; ++w) array_[w] ^= other.array_[w]; } // Set subtraction *this = v1 - v2. void BitVector::SetSubtract(const BitVector& v1, const BitVector& v2) { Alloc(v1.size()); int length = MIN(v1.WordLength(), v2.WordLength()); for (int w = 0; w < length; ++w) array_[w] = v1.array_[w] ^ (v1.array_[w] & v2.array_[w]); for (int w = WordLength() - 1; w >= length; --w) array_[w] = v1.array_[w]; } // Allocates memory for a vector of the given length. // Reallocates if the array is a different size, larger or smaller. void BitVector::Alloc(int length) { int initial_wordlength = WordLength(); bit_size_ = length; int new_wordlength = WordLength(); if (new_wordlength != initial_wordlength) { delete [] array_; array_ = new uinT32[new_wordlength]; } } } // namespace tesseract. tesseract-3.04.01/ccutil/bitvector.h000066400000000000000000000114411266071204500173150ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: bitvector.h // Description: Class replacement for BITVECTOR. // Author: Ray Smith // Created: Mon Jan 10 17:44:01 PST 2011 // // (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_CCUTIL_BITVECTOR_H__ #define TESSERACT_CCUTIL_BITVECTOR_H__ #include #include #include "host.h" namespace tesseract { // Trivial class to encapsulate a fixed-length array of bits, with // Serialize/DeSerialize. Replaces the old macros. class BitVector { public: // Fast lookup table to get the first least significant set bit in a byte. // For zero, the table has 255, but since it is a special case, most code // that uses this table will check for zero before looking up lsb_index_. static const uinT8 lsb_index_[256]; // Fast lookup table to get the residual bits after zeroing the least // significant set bit in a byte. static const uinT8 lsb_eroded_[256]; // Fast lookup table to give the number of set bits in a byte. static const int hamming_table_[256]; BitVector(); // Initializes the array to length * false. explicit BitVector(int length); BitVector(const BitVector& src); BitVector& operator=(const BitVector& src); ~BitVector(); // Initializes the array to length * false. void Init(int length); // Returns the number of bits that are accessible in the vector. int size() const { return bit_size_; } // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); void SetAllFalse(); void SetAllTrue(); // Accessors to set/reset/get bits. // The range of index is [0, size()-1]. // There is debug-only bounds checking. void SetBit(int index) { array_[WordIndex(index)] |= BitMask(index); } void ResetBit(int index) { array_[WordIndex(index)] &= ~BitMask(index); } void SetValue(int index, bool value) { if (value) SetBit(index); else ResetBit(index); } bool At(int index) const { return (array_[WordIndex(index)] & BitMask(index)) != 0; } bool operator[](int index) const { return (array_[WordIndex(index)] & BitMask(index)) != 0; } // Returns the index of the next set bit after the given index. // Useful for quickly iterating through the set bits in a sparse vector. int NextSetBit(int prev_bit) const; // Returns the number of set bits in the vector. int NumSetBits() const; // Logical in-place operations on whole bit vectors. Tries to do something // sensible if they aren't the same size, but they should be really. void operator|=(const BitVector& other); void operator&=(const BitVector& other); void operator^=(const BitVector& other); // Set subtraction *this = v1 - v2. void SetSubtract(const BitVector& v1, const BitVector& v2); private: // Allocates memory for a vector of the given length. void Alloc(int length); // Computes the index to array_ for the given index, with debug range // checking. int WordIndex(int index) const { assert(0 <= index && index < bit_size_); return index / kBitFactor; } // Returns a mask to select the appropriate bit for the given index. uinT32 BitMask(int index) const { return 1 << (index & (kBitFactor - 1)); } // Returns the number of array elements needed to represent the current // bit_size_. int WordLength() const { return (bit_size_ + kBitFactor - 1) / kBitFactor; } // Returns the number of bytes consumed by the array_. int ByteLength() const { return WordLength() * sizeof(*array_); } // Number of bits in this BitVector. inT32 bit_size_; // Array of words used to pack the bits. // Bits are stored little-endian by uinT32 word, ie by word first and then // starting with the least significant bit in each word. uinT32* array_; // Number of bits in an array_ element. static const int kBitFactor = sizeof(uinT32) * 8; }; } // namespace tesseract. #endif // TESSERACT_CCUTIL_BITVECTOR_H__ tesseract-3.04.01/ccutil/ccutil.cpp000066400000000000000000000024161266071204500171340ustar00rootroot00000000000000// Copyright 2008 Google Inc. All Rights Reserved. // Author: scharron@google.com (Samuel Charron) #include "ccutil.h" namespace tesseract { CCUtil::CCUtil() : params_(), STRING_INIT_MEMBER(m_data_sub_dir, "tessdata/", "Directory for data files", ¶ms_), #ifdef _WIN32 STRING_INIT_MEMBER(tessedit_module_name, WINDLLNAME, "Module colocated with tessdata dir", ¶ms_), #endif INT_INIT_MEMBER(ambigs_debug_level, 0, "Debug level for unichar ambiguities", ¶ms_), BOOL_MEMBER(use_definite_ambigs_for_classifier, 0, "Use definite" " ambiguities when running character classifier", ¶ms_), BOOL_MEMBER(use_ambigs_for_adaption, 0, "Use ambigs for deciding" " whether to adapt to a character", ¶ms_) { } CCUtil::~CCUtil() { } CCUtilMutex::CCUtilMutex() { #ifdef _WIN32 mutex_ = CreateMutex(0, FALSE, 0); #else pthread_mutex_init(&mutex_, NULL); #endif } void CCUtilMutex::Lock() { #ifdef _WIN32 WaitForSingleObject(mutex_, INFINITE); #else pthread_mutex_lock(&mutex_); #endif } void CCUtilMutex::Unlock() { #ifdef _WIN32 ReleaseMutex(mutex_); #else pthread_mutex_unlock(&mutex_); #endif } CCUtilMutex tprintfMutex; // should remain global } // namespace tesseract tesseract-3.04.01/ccutil/ccutil.h000066400000000000000000000053641266071204500166060ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: ccutil.h // Description: ccutil class. // Author: Samuel Charron // // (C) Copyright 2006, 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_CCUTIL_H__ #define TESSERACT_CCUTIL_CCUTIL_H__ #include "ambigs.h" #include "errcode.h" #include "strngs.h" #include "tessdatamanager.h" #include "params.h" #include "unicharset.h" #ifdef _WIN32 #include #else #include #include #endif namespace tesseract { class CCUtilMutex { public: CCUtilMutex(); void Lock(); void Unlock(); private: #ifdef _WIN32 HANDLE mutex_; #else pthread_mutex_t mutex_; #endif }; class CCUtil { public: CCUtil(); virtual ~CCUtil(); public: // Read the arguments and set up the data path. void main_setup( const char *argv0, // program name const char *basename // name of image ); ParamsVectors *params() { return ¶ms_; } STRING datadir; // dir for data files STRING imagebasename; // name of image STRING lang; STRING language_data_path_prefix; TessdataManager tessdata_manager; UNICHARSET unicharset; UnicharAmbigs unichar_ambigs; STRING imagefile; // image file name STRING directory; // main directory private: ParamsVectors params_; public: // Member parameters. // These have to be declared and initialized after params_ member, since // params_ should be initialized before parameters are added to it. STRING_VAR_H(m_data_sub_dir, "tessdata/", "Directory for data files"); #ifdef _WIN32 STRING_VAR_H(tessedit_module_name, WINDLLNAME, "Module colocated with tessdata dir"); #endif INT_VAR_H(ambigs_debug_level, 0, "Debug level for unichar ambiguities"); BOOL_VAR_H(use_definite_ambigs_for_classifier, 0, "Use definite ambiguities when running character classifier"); BOOL_VAR_H(use_ambigs_for_adaption, 0, "Use ambigs for deciding whether to adapt to a character"); }; extern CCUtilMutex tprintfMutex; // should remain global } // namespace tesseract #endif // TESSERACT_CCUTIL_CCUTIL_H__ tesseract-3.04.01/ccutil/clst.cpp000066400000000000000000000417511266071204500166230ustar00rootroot00000000000000/********************************************************************** * File: clst.c (Formerly clist.c) * Description: CONS cell list handling code which is not in the include file. * Author: Phil Cheatle * Created: Mon Jan 28 08:33:13 GMT 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include #include "clst.h" /*********************************************************************** * MEMBER FUNCTIONS OF CLASS: CLIST * ================================ **********************************************************************/ /*********************************************************************** * CLIST::internal_deep_clear * * Used by the "deep_clear" member function of derived list * classes to destroy all the elements on the list. * The calling function passes a "zapper" function which can be called to * delete each data element of the list, regardless of its class. This * technique permits a generic clear function to destroy elements of * different derived types correctly, without requiring virtual functions and * the consequential memory overhead. **********************************************************************/ void CLIST::internal_deep_clear ( //destroy all links void (*zapper) (void *)) { //ptr to zapper functn CLIST_LINK *ptr; CLIST_LINK *next; if (!empty ()) { ptr = last->next; //set to first last->next = NULL; //break circle last = NULL; //set list empty while (ptr) { next = ptr->next; zapper (ptr->data); delete(ptr); ptr = next; } } } /*********************************************************************** * CLIST::shallow_clear * * Used by the destructor and the "shallow_clear" member function of derived * list classes to destroy the list. * The data elements are NOT destroyed. * **********************************************************************/ void CLIST::shallow_clear() { //destroy all links CLIST_LINK *ptr; CLIST_LINK *next; if (!empty ()) { ptr = last->next; //set to first last->next = NULL; //break circle last = NULL; //set list empty while (ptr) { next = ptr->next; delete(ptr); ptr = next; } } } /*********************************************************************** * CLIST::assign_to_sublist * * The list is set to a sublist of another list. "This" list must be empty * before this function is invoked. The two iterators passed must refer to * the same list, different from "this" one. The sublist removed is the * inclusive list from start_it's current position to end_it's current * position. If this range passes over the end of the source list then the * source list has its end set to the previous element of start_it. The * extracted sublist is unaffected by the end point of the source list, its * end point is always the end_it position. **********************************************************************/ void CLIST::assign_to_sublist( //to this list CLIST_ITERATOR *start_it, //from list start CLIST_ITERATOR *end_it) { //from list end const ERRCODE LIST_NOT_EMPTY = "Destination list must be empty before extracting a sublist"; if (!empty ()) LIST_NOT_EMPTY.error ("CLIST.assign_to_sublist", ABORT, NULL); last = start_it->extract_sublist (end_it); } /*********************************************************************** * CLIST::length * * Return count of elements on list **********************************************************************/ inT32 CLIST::length() const { //count elements CLIST_ITERATOR it(const_cast(this)); inT32 count = 0; for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) count++; return count; } /*********************************************************************** * CLIST::sort * * Sort elements on list **********************************************************************/ void CLIST::sort ( //sort elements int comparator ( //comparison routine const void *, const void *)) { CLIST_ITERATOR it(this); inT32 count; void **base; //ptr array to sort void **current; inT32 i; /* Allocate an array of pointers, one per list element */ count = length (); base = (void **) malloc (count * sizeof (void *)); /* Extract all elements, putting the pointers in the array */ current = base; for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { *current = it.extract (); current++; } /* Sort the pointer array */ qsort ((char *) base, count, sizeof (*base), comparator); /* Rebuild the list from the sorted pointers */ current = base; for (i = 0; i < count; i++) { it.add_to_end (*current); current++; } free(base); } // Assuming list has been sorted already, insert new_data to // keep the list sorted according to the same comparison function. // Comparison function is the same as used by sort, i.e. uses double // indirection. Time is O(1) to add to beginning or end. // Time is linear to add pre-sorted items to an empty list. // If unique, then don't add duplicate entries. // Returns true if the element was added to the list. bool CLIST::add_sorted(int comparator(const void*, const void*), bool unique, void* new_data) { // Check for adding at the end. if (last == NULL || comparator(&last->data, &new_data) < 0) { CLIST_LINK* new_element = new CLIST_LINK; new_element->data = new_data; if (last == NULL) { new_element->next = new_element; } else { new_element->next = last->next; last->next = new_element; } last = new_element; return true; } else if (!unique || last->data != new_data) { // Need to use an iterator. CLIST_ITERATOR it(this); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { void* data = it.data(); if (data == new_data && unique) return false; if (comparator(&data, &new_data) > 0) break; } if (it.cycled_list()) it.add_to_end(new_data); else it.add_before_then_move(new_data); return true; } return false; } // Assuming that the minuend and subtrahend are already sorted with // the same comparison function, shallow clears this and then copies // the set difference minuend - subtrahend to this, being the elements // of minuend that do not compare equal to anything in subtrahend. // If unique is true, any duplicates in minuend are also eliminated. void CLIST::set_subtract(int comparator(const void*, const void*), bool unique, CLIST* minuend, CLIST* subtrahend) { shallow_clear(); CLIST_ITERATOR m_it(minuend); CLIST_ITERATOR s_it(subtrahend); // Since both lists are sorted, finding the subtras that are not // minus is a case of a parallel iteration. for (m_it.mark_cycle_pt(); !m_it.cycled_list(); m_it.forward()) { void* minu = m_it.data(); void* subtra = NULL; if (!s_it.empty()) { subtra = s_it.data(); while (!s_it.at_last() && comparator(&subtra, &minu) < 0) { s_it.forward(); subtra = s_it.data(); } } if (subtra == NULL || comparator(&subtra, &minu) != 0) add_sorted(comparator, unique, minu); } } /*********************************************************************** * MEMBER FUNCTIONS OF CLASS: CLIST_ITERATOR * ========================================= **********************************************************************/ /*********************************************************************** * CLIST_ITERATOR::forward * * Move the iterator to the next element of the list. * REMEMBER: ALL LISTS ARE CIRCULAR. **********************************************************************/ void *CLIST_ITERATOR::forward() { #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::forward", ABORT, NULL); #endif if (list->empty ()) return NULL; if (current) { //not removed so //set previous prev = current; started_cycling = TRUE; // In case next is deleted by another iterator, get next from current. current = current->next; } else { if (ex_current_was_cycle_pt) cycle_pt = next; current = next; } next = current->next; #ifndef NDEBUG if (!current) NULL_DATA.error ("CLIST_ITERATOR::forward", ABORT, NULL); if (!next) NULL_NEXT.error ("CLIST_ITERATOR::forward", ABORT, "This is: %p Current is: %p", this, current); #endif return current->data; } /*********************************************************************** * CLIST_ITERATOR::data_relative * * Return the data pointer to the element "offset" elements from current. * "offset" must not be less than -1. * (This function can't be INLINEd because it contains a loop) **********************************************************************/ void *CLIST_ITERATOR::data_relative( //get data + or - ... inT8 offset) { //offset from current CLIST_LINK *ptr; #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::data_relative", ABORT, NULL); if (list->empty ()) EMPTY_LIST.error ("CLIST_ITERATOR::data_relative", ABORT, NULL); if (offset < -1) BAD_PARAMETER.error ("CLIST_ITERATOR::data_relative", ABORT, "offset < -l"); #endif if (offset == -1) ptr = prev; else for (ptr = current ? current : prev; offset-- > 0; ptr = ptr->next); #ifndef NDEBUG if (!ptr) NULL_DATA.error ("CLIST_ITERATOR::data_relative", ABORT, NULL); #endif return ptr->data; } /*********************************************************************** * CLIST_ITERATOR::move_to_last() * * Move current so that it is set to the end of the list. * Return data just in case anyone wants it. * (This function can't be INLINEd because it contains a loop) **********************************************************************/ void *CLIST_ITERATOR::move_to_last() { #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::move_to_last", ABORT, NULL); #endif while (current != list->last) forward(); if (current == NULL) return NULL; else return current->data; } /*********************************************************************** * CLIST_ITERATOR::exchange() * * Given another iterator, whose current element is a different element on * the same list list OR an element of another list, exchange the two current * elements. On return, each iterator points to the element which was the * other iterators current on entry. * (This function hasn't been in-lined because its a bit big!) **********************************************************************/ void CLIST_ITERATOR::exchange( //positions of 2 links CLIST_ITERATOR *other_it) { //other iterator const ERRCODE DONT_EXCHANGE_DELETED = "Can't exchange deleted elements of lists"; CLIST_LINK *old_current; #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::exchange", ABORT, NULL); if (!other_it) BAD_PARAMETER.error ("CLIST_ITERATOR::exchange", ABORT, "other_it NULL"); if (!(other_it->list)) NO_LIST.error ("CLIST_ITERATOR::exchange", ABORT, "other_it"); #endif /* Do nothing if either list is empty or if both iterators reference the same link */ if ((list->empty ()) || (other_it->list->empty ()) || (current == other_it->current)) return; /* Error if either current element is deleted */ if (!current || !other_it->current) DONT_EXCHANGE_DELETED.error ("CLIST_ITERATOR.exchange", ABORT, NULL); /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements (other before this); non-doubleton adjacent elements (this before other); non-adjacent elements. */ //adjacent links if ((next == other_it->current) || (other_it->next == current)) { //doubleton list if ((next == other_it->current) && (other_it->next == current)) { prev = next = current; other_it->prev = other_it->next = other_it->current; } else { //non-doubleton with //adjacent links //other before this if (other_it->next == current) { other_it->prev->next = current; other_it->current->next = next; current->next = other_it->current; other_it->next = other_it->current; prev = current; } else { //this before other prev->next = other_it->current; current->next = other_it->next; other_it->current->next = current; next = current; other_it->prev = other_it->current; } } } else { //no overlap prev->next = other_it->current; current->next = other_it->next; other_it->prev->next = current; other_it->current->next = next; } /* update end of list pointer when necessary (remember that the 2 iterators may iterate over different lists!) */ if (list->last == current) list->last = other_it->current; if (other_it->list->last == other_it->current) other_it->list->last = current; if (current == cycle_pt) cycle_pt = other_it->cycle_pt; if (other_it->current == other_it->cycle_pt) other_it->cycle_pt = cycle_pt; /* The actual exchange - in all cases*/ old_current = current; current = other_it->current; other_it->current = old_current; } /*********************************************************************** * CLIST_ITERATOR::extract_sublist() * * This is a private member, used only by CLIST::assign_to_sublist. * Given another iterator for the same list, extract the links from THIS to * OTHER inclusive, link them into a new circular list, and return a * pointer to the last element. * (Can't inline this function because it contains a loop) **********************************************************************/ CLIST_LINK *CLIST_ITERATOR::extract_sublist( //from this current CLIST_ITERATOR *other_it) { //to other current CLIST_ITERATOR temp_it = *this; CLIST_LINK *end_of_new_list; const ERRCODE BAD_SUBLIST = "Can't find sublist end point in original list"; #ifndef NDEBUG const ERRCODE BAD_EXTRACTION_PTS = "Can't extract sublist from points on different lists"; const ERRCODE DONT_EXTRACT_DELETED = "Can't extract a sublist marked by deleted points"; if (!other_it) BAD_PARAMETER.error ("CLIST_ITERATOR::extract_sublist", ABORT, "other_it NULL"); if (!list) NO_LIST.error ("CLIST_ITERATOR::extract_sublist", ABORT, NULL); if (list != other_it->list) BAD_EXTRACTION_PTS.error ("CLIST_ITERATOR.extract_sublist", ABORT, NULL); if (list->empty ()) EMPTY_LIST.error ("CLIST_ITERATOR::extract_sublist", ABORT, NULL); if (!current || !other_it->current) DONT_EXTRACT_DELETED.error ("CLIST_ITERATOR.extract_sublist", ABORT, NULL); #endif ex_current_was_last = other_it->ex_current_was_last = FALSE; ex_current_was_cycle_pt = FALSE; other_it->ex_current_was_cycle_pt = FALSE; temp_it.mark_cycle_pt (); do { //walk sublist if (temp_it.cycled_list ()) //can't find end pt BAD_SUBLIST.error ("CLIST_ITERATOR.extract_sublist", ABORT, NULL); if (temp_it.at_last ()) { list->last = prev; ex_current_was_last = other_it->ex_current_was_last = TRUE; } if (temp_it.current == cycle_pt) ex_current_was_cycle_pt = TRUE; if (temp_it.current == other_it->cycle_pt) other_it->ex_current_was_cycle_pt = TRUE; temp_it.forward (); } while (temp_it.prev != other_it->current); //circularise sublist other_it->current->next = current; end_of_new_list = other_it->current; //sublist = whole list if (prev == other_it->current) { list->last = NULL; prev = current = next = NULL; other_it->prev = other_it->current = other_it->next = NULL; } else { prev->next = other_it->next; current = other_it->current = NULL; next = other_it->next; other_it->prev = prev; } return end_of_new_list; } tesseract-3.04.01/ccutil/clst.h000066400000000000000000000722761266071204500162760ustar00rootroot00000000000000/********************************************************************** * File: clst.h (Formerly clist.h) * Description: CONS cell list module include file. * Author: Phil Cheatle * Created: Mon Jan 28 08:33:13 GMT 1991 * * (C) Copyright 1991, 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 CLST_H #define CLST_H #include #include "host.h" #include "serialis.h" #include "lsterr.h" class CLIST_ITERATOR; /********************************************************************** * CLASS - CLIST_LINK * * Generic link class for singly linked CONS cell lists * * Note: No destructor - elements are assumed to be destroyed EITHER after * they have been extracted from a list OR by the CLIST destructor which * walks the list. **********************************************************************/ class DLLSYM CLIST_LINK { friend class CLIST_ITERATOR; friend class CLIST; CLIST_LINK *next; void *data; public: CLIST_LINK() { //constructor data = next = NULL; } CLIST_LINK( //copy constructor const CLIST_LINK &) { //don't copy link data = next = NULL; } void operator= ( //don't copy links const CLIST_LINK &) { data = next = NULL; } }; /********************************************************************** * CLASS - CLIST * * Generic list class for singly linked CONS cell lists **********************************************************************/ class DLLSYM CLIST { friend class CLIST_ITERATOR; CLIST_LINK *last; //End of list //(Points to head) CLIST_LINK *First() { // return first return last != NULL ? last->next : NULL; } public: CLIST() { //constructor last = NULL; } ~CLIST () { //destructor shallow_clear(); } void internal_deep_clear ( //destroy all links void (*zapper) (void *)); //ptr to zapper functn void shallow_clear(); //clear list but don't //delete data elements bool empty() const { //is list empty? return !last; } bool singleton() const { return last != NULL ? (last == last->next) : false; } void shallow_copy( //dangerous!! CLIST *from_list) { //beware destructors!! last = from_list->last; } void assign_to_sublist( //to this list CLIST_ITERATOR *start_it, //from list start CLIST_ITERATOR *end_it); //from list end inT32 length() const; //# elements in list void sort ( //sort elements int comparator ( //comparison routine const void *, const void *)); // Assuming list has been sorted already, insert new_data to // keep the list sorted according to the same comparison function. // Comparison function is the same as used by sort, i.e. uses double // indirection. Time is O(1) to add to beginning or end. // Time is linear to add pre-sorted items to an empty list. // If unique, then don't add duplicate entries. // Returns true if the element was added to the list. bool add_sorted(int comparator(const void*, const void*), bool unique, void* new_data); // Assuming that the minuend and subtrahend are already sorted with // the same comparison function, shallow clears this and then copies // the set difference minuend - subtrahend to this, being the elements // of minuend that do not compare equal to anything in subtrahend. // If unique is true, any duplicates in minuend are also eliminated. void set_subtract(int comparator(const void*, const void*), bool unique, CLIST* minuend, CLIST* subtrahend); }; /*********************************************************************** * CLASS - CLIST_ITERATOR * * Generic iterator class for singly linked lists with embedded links **********************************************************************/ class DLLSYM CLIST_ITERATOR { friend void CLIST::assign_to_sublist(CLIST_ITERATOR *, CLIST_ITERATOR *); CLIST *list; //List being iterated CLIST_LINK *prev; //prev element CLIST_LINK *current; //current element CLIST_LINK *next; //next element BOOL8 ex_current_was_last; //current extracted //was end of list BOOL8 ex_current_was_cycle_pt; //current extracted //was cycle point CLIST_LINK *cycle_pt; //point we are cycling //the list to. BOOL8 started_cycling; //Have we moved off //the start? CLIST_LINK *extract_sublist( //from this current... CLIST_ITERATOR *other_it); //to other current public: CLIST_ITERATOR() { //constructor list = NULL; } //unassigned list CLIST_ITERATOR( //constructor CLIST *list_to_iterate); void set_to_list( //change list CLIST *list_to_iterate); void add_after_then_move( //add after current & void *new_data); //move to new void add_after_stay_put( //add after current & void *new_data); //stay at current void add_before_then_move( //add before current & void *new_data); //move to new void add_before_stay_put( //add before current & void *new_data); //stay at current void add_list_after( //add a list & CLIST *list_to_add); //stay at current void add_list_before( //add a list & CLIST *list_to_add); //move to it 1st item void *data() { //get current data #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::data", ABORT, NULL); if (!current) NULL_DATA.error ("CLIST_ITERATOR::data", ABORT, NULL); #endif return current->data; } void *data_relative( //get data + or - ... inT8 offset); //offset from current void *forward(); //move to next element void *extract(); //remove from list void *move_to_first(); //go to start of list void *move_to_last(); //go to end of list void mark_cycle_pt(); //remember current BOOL8 empty() { //is list empty? #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::empty", ABORT, NULL); #endif return list->empty (); } BOOL8 current_extracted() { //current extracted? return !current; } BOOL8 at_first(); //Current is first? BOOL8 at_last(); //Current is last? BOOL8 cycled_list(); //Completed a cycle? void add_to_end( //add at end & void *new_data); //don't move void exchange( //positions of 2 links CLIST_ITERATOR *other_it); //other iterator inT32 length(); //# elements in list void sort ( //sort elements int comparator ( //comparison routine const void *, const void *)); }; /*********************************************************************** * CLIST_ITERATOR::set_to_list * * (Re-)initialise the iterator to point to the start of the list_to_iterate * over. **********************************************************************/ inline void CLIST_ITERATOR::set_to_list( //change list CLIST *list_to_iterate) { #ifndef NDEBUG if (!list_to_iterate) BAD_PARAMETER.error ("CLIST_ITERATOR::set_to_list", ABORT, "list_to_iterate is NULL"); #endif list = list_to_iterate; prev = list->last; current = list->First (); next = current != NULL ? current->next : NULL; cycle_pt = NULL; //await explicit set started_cycling = FALSE; ex_current_was_last = FALSE; ex_current_was_cycle_pt = FALSE; } /*********************************************************************** * CLIST_ITERATOR::CLIST_ITERATOR * * CONSTRUCTOR - set iterator to specified list; **********************************************************************/ inline CLIST_ITERATOR::CLIST_ITERATOR(CLIST *list_to_iterate) { set_to_list(list_to_iterate); } /*********************************************************************** * CLIST_ITERATOR::add_after_then_move * * Add a new element to the list after the current element and move the * iterator to the new element. **********************************************************************/ inline void CLIST_ITERATOR::add_after_then_move( // element to add void *new_data) { CLIST_LINK *new_element; #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::add_after_then_move", ABORT, NULL); if (!new_data) BAD_PARAMETER.error ("CLIST_ITERATOR::add_after_then_move", ABORT, "new_data is NULL"); #endif new_element = new CLIST_LINK; new_element->data = new_data; if (list->empty ()) { new_element->next = new_element; list->last = new_element; prev = next = new_element; } else { new_element->next = next; if (current) { //not extracted current->next = new_element; prev = current; if (current == list->last) list->last = new_element; } else { //current extracted prev->next = new_element; if (ex_current_was_last) list->last = new_element; if (ex_current_was_cycle_pt) cycle_pt = new_element; } } current = new_element; } /*********************************************************************** * CLIST_ITERATOR::add_after_stay_put * * Add a new element to the list after the current element but do not move * the iterator to the new element. **********************************************************************/ inline void CLIST_ITERATOR::add_after_stay_put( // element to add void *new_data) { CLIST_LINK *new_element; #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::add_after_stay_put", ABORT, NULL); if (!new_data) BAD_PARAMETER.error ("CLIST_ITERATOR::add_after_stay_put", ABORT, "new_data is NULL"); #endif new_element = new CLIST_LINK; new_element->data = new_data; if (list->empty ()) { new_element->next = new_element; list->last = new_element; prev = next = new_element; ex_current_was_last = FALSE; current = NULL; } else { new_element->next = next; if (current) { //not extracted current->next = new_element; if (prev == current) prev = new_element; if (current == list->last) list->last = new_element; } else { //current extracted prev->next = new_element; if (ex_current_was_last) { list->last = new_element; ex_current_was_last = FALSE; } } next = new_element; } } /*********************************************************************** * CLIST_ITERATOR::add_before_then_move * * Add a new element to the list before the current element and move the * iterator to the new element. **********************************************************************/ inline void CLIST_ITERATOR::add_before_then_move( // element to add void *new_data) { CLIST_LINK *new_element; #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::add_before_then_move", ABORT, NULL); if (!new_data) BAD_PARAMETER.error ("CLIST_ITERATOR::add_before_then_move", ABORT, "new_data is NULL"); #endif new_element = new CLIST_LINK; new_element->data = new_data; if (list->empty ()) { new_element->next = new_element; list->last = new_element; prev = next = new_element; } else { prev->next = new_element; if (current) { //not extracted new_element->next = current; next = current; } else { //current extracted new_element->next = next; if (ex_current_was_last) list->last = new_element; if (ex_current_was_cycle_pt) cycle_pt = new_element; } } current = new_element; } /*********************************************************************** * CLIST_ITERATOR::add_before_stay_put * * Add a new element to the list before the current element but don't move the * iterator to the new element. **********************************************************************/ inline void CLIST_ITERATOR::add_before_stay_put( // element to add void *new_data) { CLIST_LINK *new_element; #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::add_before_stay_put", ABORT, NULL); if (!new_data) BAD_PARAMETER.error ("CLIST_ITERATOR::add_before_stay_put", ABORT, "new_data is NULL"); #endif new_element = new CLIST_LINK; new_element->data = new_data; if (list->empty ()) { new_element->next = new_element; list->last = new_element; prev = next = new_element; ex_current_was_last = TRUE; current = NULL; } else { prev->next = new_element; if (current) { //not extracted new_element->next = current; if (next == current) next = new_element; } else { //current extracted new_element->next = next; if (ex_current_was_last) list->last = new_element; } prev = new_element; } } /*********************************************************************** * CLIST_ITERATOR::add_list_after * * Insert another list to this list after the current element but don't move the * iterator. **********************************************************************/ inline void CLIST_ITERATOR::add_list_after(CLIST *list_to_add) { #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::add_list_after", ABORT, NULL); if (!list_to_add) BAD_PARAMETER.error ("CLIST_ITERATOR::add_list_after", ABORT, "list_to_add is NULL"); #endif if (!list_to_add->empty ()) { if (list->empty ()) { list->last = list_to_add->last; prev = list->last; next = list->First (); ex_current_was_last = TRUE; current = NULL; } else { if (current) { //not extracted current->next = list_to_add->First (); if (current == list->last) list->last = list_to_add->last; list_to_add->last->next = next; next = current->next; } else { //current extracted prev->next = list_to_add->First (); if (ex_current_was_last) { list->last = list_to_add->last; ex_current_was_last = FALSE; } list_to_add->last->next = next; next = prev->next; } } list_to_add->last = NULL; } } /*********************************************************************** * CLIST_ITERATOR::add_list_before * * Insert another list to this list before the current element. Move the * iterator to the start of the inserted elements * iterator. **********************************************************************/ inline void CLIST_ITERATOR::add_list_before(CLIST *list_to_add) { #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::add_list_before", ABORT, NULL); if (!list_to_add) BAD_PARAMETER.error ("CLIST_ITERATOR::add_list_before", ABORT, "list_to_add is NULL"); #endif if (!list_to_add->empty ()) { if (list->empty ()) { list->last = list_to_add->last; prev = list->last; current = list->First (); next = current->next; ex_current_was_last = FALSE; } else { prev->next = list_to_add->First (); if (current) { //not extracted list_to_add->last->next = current; } else { //current extracted list_to_add->last->next = next; if (ex_current_was_last) list->last = list_to_add->last; if (ex_current_was_cycle_pt) cycle_pt = prev->next; } current = prev->next; next = current->next; } list_to_add->last = NULL; } } /*********************************************************************** * CLIST_ITERATOR::extract * * Do extraction by removing current from the list, deleting the cons cell * and returning the data to the caller, but NOT updating the iterator. (So * that any calling loop can do this.) The iterator's current points to * NULL. If the data is to be deleted, this is the callers responsibility. **********************************************************************/ inline void *CLIST_ITERATOR::extract() { void *extracted_data; #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::extract", ABORT, NULL); if (!current) //list empty or //element extracted NULL_CURRENT.error ("CLIST_ITERATOR::extract", ABORT, NULL); #endif if (list->singleton()) { // Special case where we do need to change the iterator. prev = next = list->last = NULL; } else { prev->next = next; //remove from list if (current == list->last) { list->last = prev; ex_current_was_last = TRUE; } else { ex_current_was_last = FALSE; } } // Always set ex_current_was_cycle_pt so an add/forward will work in a loop. ex_current_was_cycle_pt = (current == cycle_pt) ? TRUE : FALSE; extracted_data = current->data; delete(current); //destroy CONS cell current = NULL; return extracted_data; } /*********************************************************************** * CLIST_ITERATOR::move_to_first() * * Move current so that it is set to the start of the list. * Return data just in case anyone wants it. **********************************************************************/ inline void *CLIST_ITERATOR::move_to_first() { #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::move_to_first", ABORT, NULL); #endif current = list->First (); prev = list->last; next = current != NULL ? current->next : NULL; return current != NULL ? current->data : NULL; } /*********************************************************************** * CLIST_ITERATOR::mark_cycle_pt() * * Remember the current location so that we can tell whether we've returned * to this point later. * * If the current point is deleted either now, or in the future, the cycle * point will be set to the next item which is set to current. This could be * by a forward, add_after_then_move or add_after_then_move. **********************************************************************/ inline void CLIST_ITERATOR::mark_cycle_pt() { #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::mark_cycle_pt", ABORT, NULL); #endif if (current) cycle_pt = current; else ex_current_was_cycle_pt = TRUE; started_cycling = FALSE; } /*********************************************************************** * CLIST_ITERATOR::at_first() * * Are we at the start of the list? * **********************************************************************/ inline BOOL8 CLIST_ITERATOR::at_first() { #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::at_first", ABORT, NULL); #endif //we're at a deleted return ((list->empty ()) || (current == list->First ()) || ((current == NULL) && (prev == list->last) && //NON-last pt between !ex_current_was_last)); //first and last } /*********************************************************************** * CLIST_ITERATOR::at_last() * * Are we at the end of the list? * **********************************************************************/ inline BOOL8 CLIST_ITERATOR::at_last() { #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::at_last", ABORT, NULL); #endif //we're at a deleted return ((list->empty ()) || (current == list->last) || ((current == NULL) && (prev == list->last) && //last point between ex_current_was_last)); //first and last } /*********************************************************************** * CLIST_ITERATOR::cycled_list() * * Have we returned to the cycle_pt since it was set? * **********************************************************************/ inline BOOL8 CLIST_ITERATOR::cycled_list() { #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::cycled_list", ABORT, NULL); #endif return ((list->empty ()) || ((current == cycle_pt) && started_cycling)); } /*********************************************************************** * CLIST_ITERATOR::length() * * Return the length of the list * **********************************************************************/ inline inT32 CLIST_ITERATOR::length() { #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::length", ABORT, NULL); #endif return list->length (); } /*********************************************************************** * CLIST_ITERATOR::sort() * * Sort the elements of the list, then reposition at the start. * **********************************************************************/ inline void CLIST_ITERATOR::sort ( //sort elements int comparator ( //comparison routine const void *, const void *)) { #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::sort", ABORT, NULL); #endif list->sort (comparator); move_to_first(); } /*********************************************************************** * CLIST_ITERATOR::add_to_end * * Add a new element to the end of the list without moving the iterator. * This is provided because a single linked list cannot move to the last as * the iterator couldn't set its prev pointer. Adding to the end is * essential for implementing queues. **********************************************************************/ inline void CLIST_ITERATOR::add_to_end( // element to add void *new_data) { CLIST_LINK *new_element; #ifndef NDEBUG if (!list) NO_LIST.error ("CLIST_ITERATOR::add_to_end", ABORT, NULL); if (!new_data) BAD_PARAMETER.error ("CLIST_ITERATOR::add_to_end", ABORT, "new_data is NULL"); #endif if (this->at_last ()) { this->add_after_stay_put (new_data); } else { if (this->at_first ()) { this->add_before_stay_put (new_data); list->last = prev; } else { //Iteratr is elsewhere new_element = new CLIST_LINK; new_element->data = new_data; new_element->next = list->last->next; list->last->next = new_element; list->last = new_element; } } } /*********************************************************************** QUOTE_IT MACRO DEFINITION =========================== Replace with "". may be an arbitrary number of tokens ***********************************************************************/ #define QUOTE_IT( parm ) #parm /*********************************************************************** CLISTIZE( CLASSNAME ) MACRO DEFINITION ====================================== CLASSNAME is assumed to be the name of a class to be used in a CONS list NOTE: Because we don't use virtual functions in the list code, the list code will NOT work correctly for classes derived from this. The macro generates: - An element deletion function: CLASSNAME##_c1_zapper - An element copier function: CLASSNAME##_c1_copier - A CLIST subclass: CLASSNAME##_CLIST - A CLIST_ITERATOR subclass: CLASSNAME##_C_IT NOTE: Generated names do NOT clash with those generated by ELISTIZE, ELIST2ISE and CLIST2IZE Two macros are provided: CLISTIZE and CLISTIZEH The ...IZEH macros just define the class names for use in .h files The ...IZE macros define the code use in .c files ***********************************************************************/ /*********************************************************************** CLISTIZEH( CLASSNAME ) MACRO CLISTIZEH is a concatenation of 3 fragments CLISTIZEH_A, CLISTIZEH_B and CLISTIZEH_C. ***********************************************************************/ #define CLISTIZEH_A( CLASSNAME ) \ \ extern DLLSYM void CLASSNAME##_c1_zapper( /*delete a link*/ \ void* link); /*link to delete*/ \ \ extern DLLSYM void* CLASSNAME##_c1_copier( /*deep copy a link*/ \ void* old_element); /*source link */ #define CLISTIZEH_B( CLASSNAME ) \ \ /*********************************************************************** \ * CLASS - CLASSNAME##_CLIST \ * \ * List class for class CLASSNAME \ * \ **********************************************************************/ \ \ class DLLSYM CLASSNAME##_CLIST : public CLIST \ { \ public: \ CLASSNAME##_CLIST():CLIST() {} \ /* constructor */ \ \ CLASSNAME##_CLIST( /* don't construct */ \ const CLASSNAME##_CLIST&) /*by initial assign*/ \ { DONT_CONSTRUCT_LIST_BY_COPY.error( QUOTE_IT( CLASSNAME##_CLIST ), \ ABORT, NULL ); } \ \ void deep_clear() /* delete elements */ \ { CLIST::internal_deep_clear( &CLASSNAME##_c1_zapper ); } \ \ void operator=( /* prevent assign */ \ const CLASSNAME##_CLIST&) \ { DONT_ASSIGN_LISTS.error( QUOTE_IT( CLASSNAME##_CLIST ), \ ABORT, NULL ); } #define CLISTIZEH_C( CLASSNAME ) \ \ }; \ \ \ \ /*********************************************************************** \ * CLASS - CLASSNAME##_C_IT \ * \ * Iterator class for class CLASSNAME##_CLIST \ * \ * Note: We don't need to coerce pointers to member functions input \ * parameters as these are automatically converted to the type of the base \ * type. ("A ptr to a class may be converted to a pointer to a public base \ * class of that class") \ **********************************************************************/ \ \ class DLLSYM CLASSNAME##_C_IT : public CLIST_ITERATOR \ { \ public: \ CLASSNAME##_C_IT():CLIST_ITERATOR(){} \ \ CLASSNAME##_C_IT( \ CLASSNAME##_CLIST* list):CLIST_ITERATOR(list){} \ \ CLASSNAME* data() \ { return (CLASSNAME*) CLIST_ITERATOR::data(); } \ \ CLASSNAME* data_relative( \ inT8 offset) \ { return (CLASSNAME*) CLIST_ITERATOR::data_relative( offset ); } \ \ CLASSNAME* forward() \ { return (CLASSNAME*) CLIST_ITERATOR::forward(); } \ \ CLASSNAME* extract() \ { return (CLASSNAME*) CLIST_ITERATOR::extract(); } \ \ CLASSNAME* move_to_first() \ { return (CLASSNAME*) CLIST_ITERATOR::move_to_first(); } \ \ CLASSNAME* move_to_last() \ { return (CLASSNAME*) CLIST_ITERATOR::move_to_last(); } \ }; #define CLISTIZEH( CLASSNAME ) \ \ CLISTIZEH_A( CLASSNAME ) \ \ CLISTIZEH_B( CLASSNAME ) \ \ CLISTIZEH_C( CLASSNAME ) /*********************************************************************** CLISTIZE( CLASSNAME ) MACRO ***********************************************************************/ #define CLISTIZE( CLASSNAME ) \ \ /*********************************************************************** \ * CLASSNAME##_c1_zapper \ * \ * A function which can delete a CLASSNAME element. This is passed to the \ * generic deep_clear list member function so that when a list is cleared the \ * elements on the list are properly destroyed from the base class, even \ * though we don't use a virtual destructor function. \ **********************************************************************/ \ \ DLLSYM void CLASSNAME##_c1_zapper( /*delete a link*/ \ void* link) /*link to delete*/ \ { \ delete (CLASSNAME *) link; \ } \ #endif tesseract-3.04.01/ccutil/doubleptr.h000066400000000000000000000065771266071204500173320ustar00rootroot00000000000000// 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_ tesseract-3.04.01/ccutil/elst.cpp000066400000000000000000000367051266071204500166300ustar00rootroot00000000000000/********************************************************************** * File: elst.c (Formerly elist.c) * Description: Embedded list handling code which is not in the include file. * Author: Phil Cheatle * Created: Fri Jan 04 13:55:49 GMT 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include #include "elst.h" /*********************************************************************** * MEMBER FUNCTIONS OF CLASS: ELIST * ================================ **********************************************************************/ /*********************************************************************** * ELIST::internal_clear * * Used by the destructor and the "clear" member function of derived list * classes to destroy all the elements on the list. * The calling function passes a "zapper" function which can be called to * delete each element of the list, regardless of its derived type. This * technique permits a generic clear function to destroy elements of * different derived types correctly, without requiring virtual functions and * the consequential memory overhead. **********************************************************************/ void ELIST::internal_clear ( //destroy all links void (*zapper) (ELIST_LINK *)) { //ptr to zapper functn ELIST_LINK *ptr; ELIST_LINK *next; if (!empty ()) { ptr = last->next; //set to first last->next = NULL; //break circle last = NULL; //set list empty while (ptr) { next = ptr->next; zapper(ptr); ptr = next; } } } /*********************************************************************** * ELIST::assign_to_sublist * * The list is set to a sublist of another list. "This" list must be empty * before this function is invoked. The two iterators passed must refer to * the same list, different from "this" one. The sublist removed is the * inclusive list from start_it's current position to end_it's current * position. If this range passes over the end of the source list then the * source list has its end set to the previous element of start_it. The * extracted sublist is unaffected by the end point of the source list, its * end point is always the end_it position. **********************************************************************/ void ELIST::assign_to_sublist( //to this list ELIST_ITERATOR *start_it, //from list start ELIST_ITERATOR *end_it) { //from list end const ERRCODE LIST_NOT_EMPTY = "Destination list must be empty before extracting a sublist"; if (!empty ()) LIST_NOT_EMPTY.error ("ELIST.assign_to_sublist", ABORT, NULL); last = start_it->extract_sublist (end_it); } /*********************************************************************** * ELIST::length * * Return count of elements on list **********************************************************************/ inT32 ELIST::length() const { // count elements ELIST_ITERATOR it(const_cast(this)); inT32 count = 0; for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) count++; return count; } /*********************************************************************** * ELIST::sort * * Sort elements on list * NB If you don't like the const declarations in the comparator, coerce yours: * ( int (*)(const void *, const void *) **********************************************************************/ void ELIST::sort ( //sort elements int comparator ( //comparison routine const void *, const void *)) { ELIST_ITERATOR it(this); inT32 count; ELIST_LINK **base; //ptr array to sort ELIST_LINK **current; inT32 i; /* Allocate an array of pointers, one per list element */ count = length (); base = (ELIST_LINK **) malloc (count * sizeof (ELIST_LINK *)); /* Extract all elements, putting the pointers in the array */ current = base; for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { *current = it.extract (); current++; } /* Sort the pointer array */ qsort ((char *) base, count, sizeof (*base), comparator); /* Rebuild the list from the sorted pointers */ current = base; for (i = 0; i < count; i++) { it.add_to_end (*current); current++; } free(base); } // Assuming list has been sorted already, insert new_link to // keep the list sorted according to the same comparison function. // Comparison function is the same as used by sort, i.e. uses double // indirection. Time is O(1) to add to beginning or end. // Time is linear to add pre-sorted items to an empty list. // If unique is set to true and comparator() returns 0 (an entry with the // same information as the one contained in new_link is already in the // list) - new_link is not added to the list and the function returns the // pointer to the identical entry that already exists in the list // (otherwise the function returns new_link). ELIST_LINK *ELIST::add_sorted_and_find( int comparator(const void*, const void*), bool unique, ELIST_LINK* new_link) { // Check for adding at the end. if (last == NULL || comparator(&last, &new_link) < 0) { if (last == NULL) { new_link->next = new_link; } else { new_link->next = last->next; last->next = new_link; } last = new_link; } else { // Need to use an iterator. ELIST_ITERATOR it(this); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { ELIST_LINK* link = it.data(); int compare = comparator(&link, &new_link); if (compare > 0) { break; } else if (unique && compare == 0) { return link; } } if (it.cycled_list()) it.add_to_end(new_link); else it.add_before_then_move(new_link); } return new_link; } /*********************************************************************** * MEMBER FUNCTIONS OF CLASS: ELIST_ITERATOR * ========================================= **********************************************************************/ /*********************************************************************** * ELIST_ITERATOR::forward * * Move the iterator to the next element of the list. * REMEMBER: ALL LISTS ARE CIRCULAR. **********************************************************************/ ELIST_LINK *ELIST_ITERATOR::forward() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::forward", ABORT, NULL); #endif if (list->empty ()) return NULL; if (current) { //not removed so //set previous prev = current; started_cycling = TRUE; // In case next is deleted by another iterator, get next from current. current = current->next; } else { if (ex_current_was_cycle_pt) cycle_pt = next; current = next; } next = current->next; #ifndef NDEBUG if (!current) NULL_DATA.error ("ELIST_ITERATOR::forward", ABORT, NULL); if (!next) NULL_NEXT.error ("ELIST_ITERATOR::forward", ABORT, "This is: %p Current is: %p", this, current); #endif return current; } /*********************************************************************** * ELIST_ITERATOR::data_relative * * Return the data pointer to the element "offset" elements from current. * "offset" must not be less than -1. * (This function can't be INLINEd because it contains a loop) **********************************************************************/ ELIST_LINK *ELIST_ITERATOR::data_relative( //get data + or - ... inT8 offset) { //offset from current ELIST_LINK *ptr; #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::data_relative", ABORT, NULL); if (list->empty ()) EMPTY_LIST.error ("ELIST_ITERATOR::data_relative", ABORT, NULL); if (offset < -1) BAD_PARAMETER.error ("ELIST_ITERATOR::data_relative", ABORT, "offset < -l"); #endif if (offset == -1) ptr = prev; else for (ptr = current ? current : prev; offset-- > 0; ptr = ptr->next); #ifndef NDEBUG if (!ptr) NULL_DATA.error ("ELIST_ITERATOR::data_relative", ABORT, NULL); #endif return ptr; } /*********************************************************************** * ELIST_ITERATOR::move_to_last() * * Move current so that it is set to the end of the list. * Return data just in case anyone wants it. * (This function can't be INLINEd because it contains a loop) **********************************************************************/ ELIST_LINK *ELIST_ITERATOR::move_to_last() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::move_to_last", ABORT, NULL); #endif while (current != list->last) forward(); return current; } /*********************************************************************** * ELIST_ITERATOR::exchange() * * Given another iterator, whose current element is a different element on * the same list list OR an element of another list, exchange the two current * elements. On return, each iterator points to the element which was the * other iterators current on entry. * (This function hasn't been in-lined because its a bit big!) **********************************************************************/ void ELIST_ITERATOR::exchange( //positions of 2 links ELIST_ITERATOR *other_it) { //other iterator const ERRCODE DONT_EXCHANGE_DELETED = "Can't exchange deleted elements of lists"; ELIST_LINK *old_current; #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::exchange", ABORT, NULL); if (!other_it) BAD_PARAMETER.error ("ELIST_ITERATOR::exchange", ABORT, "other_it NULL"); if (!(other_it->list)) NO_LIST.error ("ELIST_ITERATOR::exchange", ABORT, "other_it"); #endif /* Do nothing if either list is empty or if both iterators reference the same link */ if ((list->empty ()) || (other_it->list->empty ()) || (current == other_it->current)) return; /* Error if either current element is deleted */ if (!current || !other_it->current) DONT_EXCHANGE_DELETED.error ("ELIST_ITERATOR.exchange", ABORT, NULL); /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements (other before this); non-doubleton adjacent elements (this before other); non-adjacent elements. */ //adjacent links if ((next == other_it->current) || (other_it->next == current)) { //doubleton list if ((next == other_it->current) && (other_it->next == current)) { prev = next = current; other_it->prev = other_it->next = other_it->current; } else { //non-doubleton with //adjacent links //other before this if (other_it->next == current) { other_it->prev->next = current; other_it->current->next = next; current->next = other_it->current; other_it->next = other_it->current; prev = current; } else { //this before other prev->next = other_it->current; current->next = other_it->next; other_it->current->next = current; next = current; other_it->prev = other_it->current; } } } else { //no overlap prev->next = other_it->current; current->next = other_it->next; other_it->prev->next = current; other_it->current->next = next; } /* update end of list pointer when necessary (remember that the 2 iterators may iterate over different lists!) */ if (list->last == current) list->last = other_it->current; if (other_it->list->last == other_it->current) other_it->list->last = current; if (current == cycle_pt) cycle_pt = other_it->cycle_pt; if (other_it->current == other_it->cycle_pt) other_it->cycle_pt = cycle_pt; /* The actual exchange - in all cases*/ old_current = current; current = other_it->current; other_it->current = old_current; } /*********************************************************************** * ELIST_ITERATOR::extract_sublist() * * This is a private member, used only by ELIST::assign_to_sublist. * Given another iterator for the same list, extract the links from THIS to * OTHER inclusive, link them into a new circular list, and return a * pointer to the last element. * (Can't inline this function because it contains a loop) **********************************************************************/ ELIST_LINK *ELIST_ITERATOR::extract_sublist( //from this current ELIST_ITERATOR *other_it) { //to other current #ifndef NDEBUG const ERRCODE BAD_EXTRACTION_PTS = "Can't extract sublist from points on different lists"; const ERRCODE DONT_EXTRACT_DELETED = "Can't extract a sublist marked by deleted points"; #endif const ERRCODE BAD_SUBLIST = "Can't find sublist end point in original list"; ELIST_ITERATOR temp_it = *this; ELIST_LINK *end_of_new_list; #ifndef NDEBUG if (!other_it) BAD_PARAMETER.error ("ELIST_ITERATOR::extract_sublist", ABORT, "other_it NULL"); if (!list) NO_LIST.error ("ELIST_ITERATOR::extract_sublist", ABORT, NULL); if (list != other_it->list) BAD_EXTRACTION_PTS.error ("ELIST_ITERATOR.extract_sublist", ABORT, NULL); if (list->empty ()) EMPTY_LIST.error ("ELIST_ITERATOR::extract_sublist", ABORT, NULL); if (!current || !other_it->current) DONT_EXTRACT_DELETED.error ("ELIST_ITERATOR.extract_sublist", ABORT, NULL); #endif ex_current_was_last = other_it->ex_current_was_last = FALSE; ex_current_was_cycle_pt = FALSE; other_it->ex_current_was_cycle_pt = FALSE; temp_it.mark_cycle_pt (); do { //walk sublist if (temp_it.cycled_list ()) //can't find end pt BAD_SUBLIST.error ("ELIST_ITERATOR.extract_sublist", ABORT, NULL); if (temp_it.at_last ()) { list->last = prev; ex_current_was_last = other_it->ex_current_was_last = TRUE; } if (temp_it.current == cycle_pt) ex_current_was_cycle_pt = TRUE; if (temp_it.current == other_it->cycle_pt) other_it->ex_current_was_cycle_pt = TRUE; temp_it.forward (); } while (temp_it.prev != other_it->current); //circularise sublist other_it->current->next = current; end_of_new_list = other_it->current; //sublist = whole list if (prev == other_it->current) { list->last = NULL; prev = current = next = NULL; other_it->prev = other_it->current = other_it->next = NULL; } else { prev->next = other_it->next; current = other_it->current = NULL; next = other_it->next; other_it->prev = prev; } return end_of_new_list; } tesseract-3.04.01/ccutil/elst.h000066400000000000000000001104721266071204500162670ustar00rootroot00000000000000/********************************************************************** * File: elst.h (Formerly elist.h) * Description: Embedded list module include file. * Author: Phil Cheatle * Created: Mon Jan 07 08:35:34 GMT 1991 * * (C) Copyright 1991, 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 ELST_H #define ELST_H #include #include "host.h" #include "serialis.h" #include "lsterr.h" class ELIST_ITERATOR; /********************************************************************** This module implements list classes and iterators. The following list types and iterators are provided: List type List Class Iterator Class Element Class --------- ---------- -------------- ------------- Embedded list ELIST ELIST_ITERATOR ELIST_LINK (Single linked) Embedded list ELIST2 ELIST2_ITERATOR ELIST2_LINK (Double linked) Cons List CLIST CLIST_ITERATOR CLIST_LINK (Single linked) Cons List CLIST2 CLIST2_ITERATOR CLIST2_LINK (Double linked) An embedded list is where the list pointers are provided by a generic class. Data types to be listed inherit from the generic class. Data is thus linked in only ONE list at any one time. A cons list has a separate structure for a "cons cell". This contains the list pointer(s) AND a pointer to the data structure held on the list. A structure can be on many cons lists at the same time, and the structure does not need to inherit from any generic class in order to be on the list. The implementation of lists is very careful about space and speed overheads. This is why many embedded lists are provided. The same concerns mean that in-line type coercion is done, rather than use virtual functions. This is cumbersome in that each data type to be listed requires its own iterator and list class - though macros can gererate these. It also prevents heterogeneous lists. **********************************************************************/ /********************************************************************** * CLASS - ELIST_LINK * * Generic link class for singly linked lists with embedded links * * Note: No destructor - elements are assumed to be destroyed EITHER after * they have been extracted from a list OR by the ELIST destructor which * walks the list. **********************************************************************/ class DLLSYM ELIST_LINK { friend class ELIST_ITERATOR; friend class ELIST; ELIST_LINK *next; public: ELIST_LINK() { next = NULL; } //constructor ELIST_LINK(const ELIST_LINK &) { // don't copy link. next = NULL; } void operator= ( //don't copy links const ELIST_LINK &) { next = NULL; } }; /********************************************************************** * CLASS - ELIST * * Generic list class for singly linked lists with embedded links **********************************************************************/ class DLLSYM ELIST { friend class ELIST_ITERATOR; ELIST_LINK *last; //End of list //(Points to head) ELIST_LINK *First() { // return first return last ? last->next : NULL; } public: ELIST() { //constructor last = NULL; } void internal_clear ( //destroy all links //ptr to zapper functn void (*zapper) (ELIST_LINK *)); bool empty() const { //is list empty? return !last; } bool singleton() const { return last ? (last == last->next) : false; } void shallow_copy( //dangerous!! ELIST *from_list) { //beware destructors!! last = from_list->last; } //ptr to copier functn void internal_deep_copy (ELIST_LINK * (*copier) (ELIST_LINK *), const ELIST * list); //list being copied void assign_to_sublist( //to this list ELIST_ITERATOR *start_it, //from list start ELIST_ITERATOR *end_it); //from list end inT32 length() const; // # elements in list void sort ( //sort elements int comparator ( //comparison routine const void *, const void *)); // Assuming list has been sorted already, insert new_link to // keep the list sorted according to the same comparison function. // Comparison function is the same as used by sort, i.e. uses double // indirection. Time is O(1) to add to beginning or end. // Time is linear to add pre-sorted items to an empty list. // If unique is set to true and comparator() returns 0 (an entry with the // same information as the one contained in new_link is already in the // list) - new_link is not added to the list and the function returns the // pointer to the identical entry that already exists in the list // (otherwise the function returns new_link). ELIST_LINK *add_sorted_and_find(int comparator(const void*, const void*), bool unique, ELIST_LINK* new_link); // Same as above, but returns true if the new entry was inserted, false // if the identical entry already existed in the list. bool add_sorted(int comparator(const void*, const void*), bool unique, ELIST_LINK* new_link) { return (add_sorted_and_find(comparator, unique, new_link) == new_link); } }; /*********************************************************************** * CLASS - ELIST_ITERATOR * * Generic iterator class for singly linked lists with embedded links **********************************************************************/ class DLLSYM ELIST_ITERATOR { friend void ELIST::assign_to_sublist(ELIST_ITERATOR *, ELIST_ITERATOR *); ELIST *list; //List being iterated ELIST_LINK *prev; //prev element ELIST_LINK *current; //current element ELIST_LINK *next; //next element bool ex_current_was_last; //current extracted //was end of list bool ex_current_was_cycle_pt; //current extracted //was cycle point ELIST_LINK *cycle_pt; //point we are cycling //the list to. bool started_cycling; //Have we moved off //the start? ELIST_LINK *extract_sublist( //from this current... ELIST_ITERATOR *other_it); //to other current public: ELIST_ITERATOR() { //constructor list = NULL; } //unassigned list explicit ELIST_ITERATOR(ELIST *list_to_iterate); void set_to_list( //change list ELIST *list_to_iterate); void add_after_then_move( //add after current & ELIST_LINK *new_link); //move to new void add_after_stay_put( //add after current & ELIST_LINK *new_link); //stay at current void add_before_then_move( //add before current & ELIST_LINK *new_link); //move to new void add_before_stay_put( //add before current & ELIST_LINK *new_link); //stay at current void add_list_after( //add a list & ELIST *list_to_add); //stay at current void add_list_before( //add a list & ELIST *list_to_add); //move to it 1st item ELIST_LINK *data() { //get current data #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::data", ABORT, NULL); if (!current) NULL_DATA.error ("ELIST_ITERATOR::data", ABORT, NULL); #endif return current; } ELIST_LINK *data_relative( //get data + or - ... inT8 offset); //offset from current ELIST_LINK *forward(); //move to next element ELIST_LINK *extract(); //remove from list ELIST_LINK *move_to_first(); //go to start of list ELIST_LINK *move_to_last(); //go to end of list void mark_cycle_pt(); //remember current bool empty() { //is list empty? #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::empty", ABORT, NULL); #endif return list->empty (); } bool current_extracted() { //current extracted? return !current; } bool at_first(); //Current is first? bool at_last(); //Current is last? bool cycled_list(); //Completed a cycle? void add_to_end( //add at end & ELIST_LINK *new_link); //don't move void exchange( //positions of 2 links ELIST_ITERATOR *other_it); //other iterator inT32 length(); //# elements in list void sort ( //sort elements int comparator ( //comparison routine const void *, const void *)); }; /*********************************************************************** * ELIST_ITERATOR::set_to_list * * (Re-)initialise the iterator to point to the start of the list_to_iterate * over. **********************************************************************/ inline void ELIST_ITERATOR::set_to_list( //change list ELIST *list_to_iterate) { #ifndef NDEBUG if (!list_to_iterate) BAD_PARAMETER.error ("ELIST_ITERATOR::set_to_list", ABORT, "list_to_iterate is NULL"); #endif list = list_to_iterate; prev = list->last; current = list->First (); next = current ? current->next : NULL; cycle_pt = NULL; //await explicit set started_cycling = FALSE; ex_current_was_last = FALSE; ex_current_was_cycle_pt = FALSE; } /*********************************************************************** * ELIST_ITERATOR::ELIST_ITERATOR * * CONSTRUCTOR - set iterator to specified list; **********************************************************************/ inline ELIST_ITERATOR::ELIST_ITERATOR(ELIST *list_to_iterate) { set_to_list(list_to_iterate); } /*********************************************************************** * ELIST_ITERATOR::add_after_then_move * * Add a new element to the list after the current element and move the * iterator to the new element. **********************************************************************/ inline void ELIST_ITERATOR::add_after_then_move( // element to add ELIST_LINK *new_element) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::add_after_then_move", ABORT, NULL); if (!new_element) BAD_PARAMETER.error ("ELIST_ITERATOR::add_after_then_move", ABORT, "new_element is NULL"); if (new_element->next) STILL_LINKED.error ("ELIST_ITERATOR::add_after_then_move", ABORT, NULL); #endif if (list->empty ()) { new_element->next = new_element; list->last = new_element; prev = next = new_element; } else { new_element->next = next; if (current) { //not extracted current->next = new_element; prev = current; if (current == list->last) list->last = new_element; } else { //current extracted prev->next = new_element; if (ex_current_was_last) list->last = new_element; if (ex_current_was_cycle_pt) cycle_pt = new_element; } } current = new_element; } /*********************************************************************** * ELIST_ITERATOR::add_after_stay_put * * Add a new element to the list after the current element but do not move * the iterator to the new element. **********************************************************************/ inline void ELIST_ITERATOR::add_after_stay_put( // element to add ELIST_LINK *new_element) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::add_after_stay_put", ABORT, NULL); if (!new_element) BAD_PARAMETER.error ("ELIST_ITERATOR::add_after_stay_put", ABORT, "new_element is NULL"); if (new_element->next) STILL_LINKED.error ("ELIST_ITERATOR::add_after_stay_put", ABORT, NULL); #endif if (list->empty ()) { new_element->next = new_element; list->last = new_element; prev = next = new_element; ex_current_was_last = FALSE; current = NULL; } else { new_element->next = next; if (current) { //not extracted current->next = new_element; if (prev == current) prev = new_element; if (current == list->last) list->last = new_element; } else { //current extracted prev->next = new_element; if (ex_current_was_last) { list->last = new_element; ex_current_was_last = FALSE; } } next = new_element; } } /*********************************************************************** * ELIST_ITERATOR::add_before_then_move * * Add a new element to the list before the current element and move the * iterator to the new element. **********************************************************************/ inline void ELIST_ITERATOR::add_before_then_move( // element to add ELIST_LINK *new_element) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::add_before_then_move", ABORT, NULL); if (!new_element) BAD_PARAMETER.error ("ELIST_ITERATOR::add_before_then_move", ABORT, "new_element is NULL"); if (new_element->next) STILL_LINKED.error ("ELIST_ITERATOR::add_before_then_move", ABORT, NULL); #endif if (list->empty ()) { new_element->next = new_element; list->last = new_element; prev = next = new_element; } else { prev->next = new_element; if (current) { //not extracted new_element->next = current; next = current; } else { //current extracted new_element->next = next; if (ex_current_was_last) list->last = new_element; if (ex_current_was_cycle_pt) cycle_pt = new_element; } } current = new_element; } /*********************************************************************** * ELIST_ITERATOR::add_before_stay_put * * Add a new element to the list before the current element but don't move the * iterator to the new element. **********************************************************************/ inline void ELIST_ITERATOR::add_before_stay_put( // element to add ELIST_LINK *new_element) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::add_before_stay_put", ABORT, NULL); if (!new_element) BAD_PARAMETER.error ("ELIST_ITERATOR::add_before_stay_put", ABORT, "new_element is NULL"); if (new_element->next) STILL_LINKED.error ("ELIST_ITERATOR::add_before_stay_put", ABORT, NULL); #endif if (list->empty ()) { new_element->next = new_element; list->last = new_element; prev = next = new_element; ex_current_was_last = TRUE; current = NULL; } else { prev->next = new_element; if (current) { //not extracted new_element->next = current; if (next == current) next = new_element; } else { //current extracted new_element->next = next; if (ex_current_was_last) list->last = new_element; } prev = new_element; } } /*********************************************************************** * ELIST_ITERATOR::add_list_after * * Insert another list to this list after the current element but don't move the * iterator. **********************************************************************/ inline void ELIST_ITERATOR::add_list_after(ELIST *list_to_add) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::add_list_after", ABORT, NULL); if (!list_to_add) BAD_PARAMETER.error ("ELIST_ITERATOR::add_list_after", ABORT, "list_to_add is NULL"); #endif if (!list_to_add->empty ()) { if (list->empty ()) { list->last = list_to_add->last; prev = list->last; next = list->First (); ex_current_was_last = TRUE; current = NULL; } else { if (current) { //not extracted current->next = list_to_add->First (); if (current == list->last) list->last = list_to_add->last; list_to_add->last->next = next; next = current->next; } else { //current extracted prev->next = list_to_add->First (); if (ex_current_was_last) { list->last = list_to_add->last; ex_current_was_last = FALSE; } list_to_add->last->next = next; next = prev->next; } } list_to_add->last = NULL; } } /*********************************************************************** * ELIST_ITERATOR::add_list_before * * Insert another list to this list before the current element. Move the * iterator to the start of the inserted elements * iterator. **********************************************************************/ inline void ELIST_ITERATOR::add_list_before(ELIST *list_to_add) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::add_list_before", ABORT, NULL); if (!list_to_add) BAD_PARAMETER.error ("ELIST_ITERATOR::add_list_before", ABORT, "list_to_add is NULL"); #endif if (!list_to_add->empty ()) { if (list->empty ()) { list->last = list_to_add->last; prev = list->last; current = list->First (); next = current->next; ex_current_was_last = FALSE; } else { prev->next = list_to_add->First (); if (current) { //not extracted list_to_add->last->next = current; } else { //current extracted list_to_add->last->next = next; if (ex_current_was_last) list->last = list_to_add->last; if (ex_current_was_cycle_pt) cycle_pt = prev->next; } current = prev->next; next = current->next; } list_to_add->last = NULL; } } /*********************************************************************** * ELIST_ITERATOR::extract * * Do extraction by removing current from the list, returning it to the * caller, but NOT updating the iterator. (So that any calling loop can do * this.) The iterator's current points to NULL. If the extracted element * is to be deleted, this is the callers responsibility. **********************************************************************/ inline ELIST_LINK *ELIST_ITERATOR::extract() { ELIST_LINK *extracted_link; #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::extract", ABORT, NULL); if (!current) //list empty or //element extracted NULL_CURRENT.error ("ELIST_ITERATOR::extract", ABORT, NULL); #endif if (list->singleton()) { // Special case where we do need to change the iterator. prev = next = list->last = NULL; } else { prev->next = next; //remove from list if (current == list->last) { list->last = prev; ex_current_was_last = TRUE; } else { ex_current_was_last = FALSE; } } // Always set ex_current_was_cycle_pt so an add/forward will work in a loop. ex_current_was_cycle_pt = (current == cycle_pt) ? TRUE : FALSE; extracted_link = current; extracted_link->next = NULL; //for safety current = NULL; return extracted_link; } /*********************************************************************** * ELIST_ITERATOR::move_to_first() * * Move current so that it is set to the start of the list. * Return data just in case anyone wants it. **********************************************************************/ inline ELIST_LINK *ELIST_ITERATOR::move_to_first() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::move_to_first", ABORT, NULL); #endif current = list->First (); prev = list->last; next = current ? current->next : NULL; return current; } /*********************************************************************** * ELIST_ITERATOR::mark_cycle_pt() * * Remember the current location so that we can tell whether we've returned * to this point later. * * If the current point is deleted either now, or in the future, the cycle * point will be set to the next item which is set to current. This could be * by a forward, add_after_then_move or add_after_then_move. **********************************************************************/ inline void ELIST_ITERATOR::mark_cycle_pt() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::mark_cycle_pt", ABORT, NULL); #endif if (current) cycle_pt = current; else ex_current_was_cycle_pt = TRUE; started_cycling = FALSE; } /*********************************************************************** * ELIST_ITERATOR::at_first() * * Are we at the start of the list? * **********************************************************************/ inline bool ELIST_ITERATOR::at_first() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::at_first", ABORT, NULL); #endif //we're at a deleted return ((list->empty ()) || (current == list->First ()) || ((current == NULL) && (prev == list->last) && //NON-last pt between !ex_current_was_last)); //first and last } /*********************************************************************** * ELIST_ITERATOR::at_last() * * Are we at the end of the list? * **********************************************************************/ inline bool ELIST_ITERATOR::at_last() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::at_last", ABORT, NULL); #endif //we're at a deleted return ((list->empty ()) || (current == list->last) || ((current == NULL) && (prev == list->last) && //last point between ex_current_was_last)); //first and last } /*********************************************************************** * ELIST_ITERATOR::cycled_list() * * Have we returned to the cycle_pt since it was set? * **********************************************************************/ inline bool ELIST_ITERATOR::cycled_list() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::cycled_list", ABORT, NULL); #endif return ((list->empty ()) || ((current == cycle_pt) && started_cycling)); } /*********************************************************************** * ELIST_ITERATOR::length() * * Return the length of the list * **********************************************************************/ inline inT32 ELIST_ITERATOR::length() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::length", ABORT, NULL); #endif return list->length (); } /*********************************************************************** * ELIST_ITERATOR::sort() * * Sort the elements of the list, then reposition at the start. * **********************************************************************/ inline void ELIST_ITERATOR::sort ( //sort elements int comparator ( //comparison routine const void *, const void *)) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::sort", ABORT, NULL); #endif list->sort (comparator); move_to_first(); } /*********************************************************************** * ELIST_ITERATOR::add_to_end * * Add a new element to the end of the list without moving the iterator. * This is provided because a single linked list cannot move to the last as * the iterator couldn't set its prev pointer. Adding to the end is * essential for implementing queues. **********************************************************************/ inline void ELIST_ITERATOR::add_to_end( // element to add ELIST_LINK *new_element) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST_ITERATOR::add_to_end", ABORT, NULL); if (!new_element) BAD_PARAMETER.error ("ELIST_ITERATOR::add_to_end", ABORT, "new_element is NULL"); if (new_element->next) STILL_LINKED.error ("ELIST_ITERATOR::add_to_end", ABORT, NULL); #endif if (this->at_last ()) { this->add_after_stay_put (new_element); } else { if (this->at_first ()) { this->add_before_stay_put (new_element); list->last = new_element; } else { //Iteratr is elsewhere new_element->next = list->last->next; list->last->next = new_element; list->last = new_element; } } } /*********************************************************************** ******************** MACROS ************************************** ***********************************************************************/ /*********************************************************************** QUOTE_IT MACRO DEFINITION =========================== Replace with "". may be an arbitrary number of tokens ***********************************************************************/ #define QUOTE_IT( parm ) #parm /*********************************************************************** ELISTIZE( CLASSNAME ) MACRO ============================ CLASSNAME is assumed to be the name of a class which has a baseclass of ELIST_LINK. NOTE: Because we don't use virtual functions in the list code, the list code will NOT work correctly for classes derived from this. The macros generate: - An element deletion function: CLASSNAME##_zapper - An E_LIST subclass: CLASSNAME##_LIST - An E_LIST_ITERATOR subclass: CLASSNAME##_IT NOTE: Generated names are DELIBERATELY designed to clash with those for ELIST2IZE but NOT with those for CLISTIZE and CLIST2IZE Two macros are provided: ELISTIZE and ELISTIZEH. The ...IZEH macros just define the class names for use in .h files The ...IZE macros define the code use in .c files ***********************************************************************/ /*********************************************************************** ELISTIZEH( CLASSNAME ) MACRO ELISTIZEH is a concatenation of 3 fragments ELISTIZEH_A, ELISTIZEH_B and ELISTIZEH_C. ***********************************************************************/ #define ELISTIZEH_A(CLASSNAME) \ \ extern DLLSYM void CLASSNAME##_zapper(ELIST_LINK* link); #define ELISTIZEH_B(CLASSNAME) \ \ /*********************************************************************** \ * CLASS - CLASSNAME##_LIST \ * \ * List class for class CLASSNAME \ * \ **********************************************************************/ \ \ class DLLSYM CLASSNAME##_LIST : public ELIST { \ public: \ CLASSNAME##_LIST():ELIST() {} \ \ void clear() { /* delete elements */\ ELIST::internal_clear(&CLASSNAME##_zapper); \ } \ \ ~CLASSNAME##_LIST() { \ clear(); \ } \ \ /* Become a deep copy of src_list*/ \ void deep_copy(const CLASSNAME##_LIST* src_list, \ CLASSNAME* (*copier)(const CLASSNAME*)); \ \ private: \ /* Prevent assign and copy construction. */ \ CLASSNAME##_LIST(const CLASSNAME##_LIST&) { \ DONT_CONSTRUCT_LIST_BY_COPY.error(QUOTE_IT(CLASSNAME##_LIST), ABORT, NULL);\ } \ void operator=(const CLASSNAME##_LIST&) { \ DONT_ASSIGN_LISTS.error(QUOTE_IT(CLASSNAME##_LIST), ABORT, NULL ); \ } \ #define ELISTIZEH_C( CLASSNAME ) \ }; \ \ \ \ /*********************************************************************** \ * CLASS - CLASSNAME##_IT \ * \ * Iterator class for class CLASSNAME##_LIST \ * \ * Note: We don't need to coerce pointers to member functions input \ * parameters as these are automatically converted to the type of the base \ * type. ("A ptr to a class may be converted to a pointer to a public base \ * class of that class") \ **********************************************************************/ \ \ class DLLSYM CLASSNAME##_IT : public ELIST_ITERATOR { \ public: \ CLASSNAME##_IT():ELIST_ITERATOR(){} \ \ /* TODO(rays) This constructor should be explicit, but that means changing \ hundreds of incorrect initializations of iterators that use = over () */ \ CLASSNAME##_IT(CLASSNAME##_LIST* list) : ELIST_ITERATOR(list) {} \ \ CLASSNAME* data() { \ return reinterpret_cast(ELIST_ITERATOR::data()); \ } \ \ CLASSNAME* data_relative(inT8 offset) { \ return reinterpret_cast(ELIST_ITERATOR::data_relative(offset));\ } \ \ CLASSNAME* forward() { \ return reinterpret_cast(ELIST_ITERATOR::forward()); \ } \ \ CLASSNAME* extract() { \ return reinterpret_cast(ELIST_ITERATOR::extract()); \ } \ \ CLASSNAME* move_to_first() { \ return reinterpret_cast(ELIST_ITERATOR::move_to_first()); \ } \ \ CLASSNAME* move_to_last() { \ return reinterpret_cast(ELIST_ITERATOR::move_to_last()); \ } \ }; #define ELISTIZEH( CLASSNAME ) \ \ ELISTIZEH_A( CLASSNAME ) \ \ ELISTIZEH_B( CLASSNAME ) \ \ ELISTIZEH_C( CLASSNAME ) /*********************************************************************** ELISTIZE( CLASSNAME ) MACRO ***********************************************************************/ #define ELISTIZE(CLASSNAME) \ \ /*********************************************************************** \ * CLASSNAME##_zapper \ * \ * A function which can delete a CLASSNAME element. This is passed to the \ * generic clear list member function so that when a list is cleared the \ * elements on the list are properly destroyed from the base class, even \ * though we don't use a virtual destructor function. \ **********************************************************************/ \ \ DLLSYM void CLASSNAME##_zapper(ELIST_LINK* link) { \ delete reinterpret_cast(link); \ } \ \ /* Become a deep copy of src_list*/ \ void CLASSNAME##_LIST::deep_copy(const CLASSNAME##_LIST* src_list, \ CLASSNAME* (*copier)(const CLASSNAME*)) { \ \ CLASSNAME##_IT from_it(const_cast(src_list)); \ CLASSNAME##_IT to_it(this); \ \ for (from_it.mark_cycle_pt(); !from_it.cycled_list(); from_it.forward()) \ to_it.add_after_then_move((*copier)(from_it.data())); \ } #endif tesseract-3.04.01/ccutil/elst2.cpp000066400000000000000000000402101266071204500166740ustar00rootroot00000000000000/********************************************************************** * File: elst2.c (Formerly elist2.c) * Description: Doubly linked embedded list code not in the include file. * Author: Phil Cheatle * Created: Wed Jan 23 11:04:47 GMT 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include #include "host.h" #include "elst2.h" /*********************************************************************** * MEMBER FUNCTIONS OF CLASS: ELIST2 * ================================= **********************************************************************/ /*********************************************************************** * ELIST2::internal_clear * * Used by the destructor and the "clear" member function of derived list * classes to destroy all the elements on the list. * The calling function passes a "zapper" function which can be called to * delete each element of the list, regardless of its derived type. This * technique permits a generic clear function to destroy elements of * different derived types correctly, without requiring virtual functions and * the consequential memory overhead. **********************************************************************/ void ELIST2::internal_clear ( //destroy all links void (*zapper) (ELIST2_LINK *)) { //ptr to zapper functn ELIST2_LINK *ptr; ELIST2_LINK *next; if (!empty ()) { ptr = last->next; //set to first last->next = NULL; //break circle last = NULL; //set list empty while (ptr) { next = ptr->next; zapper(ptr); ptr = next; } } } /*********************************************************************** * ELIST2::assign_to_sublist * * The list is set to a sublist of another list. "This" list must be empty * before this function is invoked. The two iterators passed must refer to * the same list, different from "this" one. The sublist removed is the * inclusive list from start_it's current position to end_it's current * position. If this range passes over the end of the source list then the * source list has its end set to the previous element of start_it. The * extracted sublist is unaffected by the end point of the source list, its * end point is always the end_it position. **********************************************************************/ void ELIST2::assign_to_sublist( //to this list ELIST2_ITERATOR *start_it, //from list start ELIST2_ITERATOR *end_it) { //from list end const ERRCODE LIST_NOT_EMPTY = "Destination list must be empty before extracting a sublist"; if (!empty ()) LIST_NOT_EMPTY.error ("ELIST2.assign_to_sublist", ABORT, NULL); last = start_it->extract_sublist (end_it); } /*********************************************************************** * ELIST2::length * * Return count of elements on list **********************************************************************/ inT32 ELIST2::length() const { // count elements ELIST2_ITERATOR it(const_cast(this)); inT32 count = 0; for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) count++; return count; } /*********************************************************************** * ELIST2::sort * * Sort elements on list * NB If you don't like the const declarations in the comparator, coerce yours: * ( int (*)(const void *, const void *) **********************************************************************/ void ELIST2::sort ( //sort elements int comparator ( //comparison routine const void *, const void *)) { ELIST2_ITERATOR it(this); inT32 count; ELIST2_LINK **base; //ptr array to sort ELIST2_LINK **current; inT32 i; /* Allocate an array of pointers, one per list element */ count = length (); base = (ELIST2_LINK **) malloc (count * sizeof (ELIST2_LINK *)); /* Extract all elements, putting the pointers in the array */ current = base; for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { *current = it.extract (); current++; } /* Sort the pointer array */ qsort ((char *) base, count, sizeof (*base), comparator); /* Rebuild the list from the sorted pointers */ current = base; for (i = 0; i < count; i++) { it.add_to_end (*current); current++; } free(base); } // Assuming list has been sorted already, insert new_link to // keep the list sorted according to the same comparison function. // Comparison function is the same as used by sort, i.e. uses double // indirection. Time is O(1) to add to beginning or end. // Time is linear to add pre-sorted items to an empty list. void ELIST2::add_sorted(int comparator(const void*, const void*), ELIST2_LINK* new_link) { // Check for adding at the end. if (last == NULL || comparator(&last, &new_link) < 0) { if (last == NULL) { new_link->next = new_link; new_link->prev = new_link; } else { new_link->next = last->next; new_link->prev = last; last->next = new_link; new_link->next->prev = new_link; } last = new_link; } else { // Need to use an iterator. ELIST2_ITERATOR it(this); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { ELIST2_LINK* link = it.data(); if (comparator(&link, &new_link) > 0) break; } if (it.cycled_list()) it.add_to_end(new_link); else it.add_before_then_move(new_link); } } /*********************************************************************** * MEMBER FUNCTIONS OF CLASS: ELIST2_ITERATOR * ========================================== **********************************************************************/ /*********************************************************************** * ELIST2_ITERATOR::forward * * Move the iterator to the next element of the list. * REMEMBER: ALL LISTS ARE CIRCULAR. **********************************************************************/ ELIST2_LINK *ELIST2_ITERATOR::forward() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::forward", ABORT, NULL); #endif if (list->empty ()) return NULL; if (current) { //not removed so //set previous prev = current; started_cycling = TRUE; // In case next is deleted by another iterator, get it from the current. current = current->next; } else { if (ex_current_was_cycle_pt) cycle_pt = next; current = next; } next = current->next; #ifndef NDEBUG if (!current) NULL_DATA.error ("ELIST2_ITERATOR::forward", ABORT, NULL); if (!next) NULL_NEXT.error ("ELIST2_ITERATOR::forward", ABORT, "This is: %p Current is: %p", this, current); #endif return current; } /*********************************************************************** * ELIST2_ITERATOR::backward * * Move the iterator to the previous element of the list. * REMEMBER: ALL LISTS ARE CIRCULAR. **********************************************************************/ ELIST2_LINK *ELIST2_ITERATOR::backward() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::backward", ABORT, NULL); #endif if (list->empty ()) return NULL; if (current) { //not removed so //set previous next = current; started_cycling = TRUE; // In case prev is deleted by another iterator, get it from current. current = current->prev; } else { if (ex_current_was_cycle_pt) cycle_pt = prev; current = prev; } prev = current->prev; #ifndef NDEBUG if (!current) NULL_DATA.error ("ELIST2_ITERATOR::backward", ABORT, NULL); if (!prev) NULL_PREV.error ("ELIST2_ITERATOR::backward", ABORT, "This is: %p Current is: %p", this, current); #endif return current; } /*********************************************************************** * ELIST2_ITERATOR::data_relative * * Return the data pointer to the element "offset" elements from current. * (This function can't be INLINEd because it contains a loop) **********************************************************************/ ELIST2_LINK *ELIST2_ITERATOR::data_relative( //get data + or - .. inT8 offset) { //offset from current ELIST2_LINK *ptr; #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::data_relative", ABORT, NULL); if (list->empty ()) EMPTY_LIST.error ("ELIST2_ITERATOR::data_relative", ABORT, NULL); #endif if (offset < 0) for (ptr = current ? current : next; offset++ < 0; ptr = ptr->prev); else for (ptr = current ? current : prev; offset-- > 0; ptr = ptr->next); #ifndef NDEBUG if (!ptr) NULL_DATA.error ("ELIST2_ITERATOR::data_relative", ABORT, NULL); #endif return ptr; } /*********************************************************************** * ELIST2_ITERATOR::exchange() * * Given another iterator, whose current element is a different element on * the same list list OR an element of another list, exchange the two current * elements. On return, each iterator points to the element which was the * other iterators current on entry. * (This function hasn't been in-lined because its a bit big!) **********************************************************************/ void ELIST2_ITERATOR::exchange( //positions of 2 links ELIST2_ITERATOR *other_it) { //other iterator const ERRCODE DONT_EXCHANGE_DELETED = "Can't exchange deleted elements of lists"; ELIST2_LINK *old_current; #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::exchange", ABORT, NULL); if (!other_it) BAD_PARAMETER.error ("ELIST2_ITERATOR::exchange", ABORT, "other_it NULL"); if (!(other_it->list)) NO_LIST.error ("ELIST2_ITERATOR::exchange", ABORT, "other_it"); #endif /* Do nothing if either list is empty or if both iterators reference the same link */ if ((list->empty ()) || (other_it->list->empty ()) || (current == other_it->current)) return; /* Error if either current element is deleted */ if (!current || !other_it->current) DONT_EXCHANGE_DELETED.error ("ELIST2_ITERATOR.exchange", ABORT, NULL); /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements (other before this); non-doubleton adjacent elements (this before other); non-adjacent elements. */ //adjacent links if ((next == other_it->current) || (other_it->next == current)) { //doubleton list if ((next == other_it->current) && (other_it->next == current)) { prev = next = current; other_it->prev = other_it->next = other_it->current; } else { //non-doubleton with //adjacent links //other before this if (other_it->next == current) { other_it->prev->next = current; other_it->current->next = next; other_it->current->prev = current; current->next = other_it->current; current->prev = other_it->prev; next->prev = other_it->current; other_it->next = other_it->current; prev = current; } else { //this before other prev->next = other_it->current; current->next = other_it->next; current->prev = other_it->current; other_it->current->next = current; other_it->current->prev = prev; other_it->next->prev = current; next = current; other_it->prev = other_it->current; } } } else { //no overlap prev->next = other_it->current; current->next = other_it->next; current->prev = other_it->prev; next->prev = other_it->current; other_it->prev->next = current; other_it->current->next = next; other_it->current->prev = prev; other_it->next->prev = current; } /* update end of list pointer when necessary (remember that the 2 iterators may iterate over different lists!) */ if (list->last == current) list->last = other_it->current; if (other_it->list->last == other_it->current) other_it->list->last = current; if (current == cycle_pt) cycle_pt = other_it->cycle_pt; if (other_it->current == other_it->cycle_pt) other_it->cycle_pt = cycle_pt; /* The actual exchange - in all cases*/ old_current = current; current = other_it->current; other_it->current = old_current; } /*********************************************************************** * ELIST2_ITERATOR::extract_sublist() * * This is a private member, used only by ELIST2::assign_to_sublist. * Given another iterator for the same list, extract the links from THIS to * OTHER inclusive, link them into a new circular list, and return a * pointer to the last element. * (Can't inline this function because it contains a loop) **********************************************************************/ ELIST2_LINK *ELIST2_ITERATOR::extract_sublist( //from this current ELIST2_ITERATOR *other_it) { //to other current #ifndef NDEBUG const ERRCODE BAD_EXTRACTION_PTS = "Can't extract sublist from points on different lists"; const ERRCODE DONT_EXTRACT_DELETED = "Can't extract a sublist marked by deleted points"; #endif const ERRCODE BAD_SUBLIST = "Can't find sublist end point in original list"; ELIST2_ITERATOR temp_it = *this; ELIST2_LINK *end_of_new_list; #ifndef NDEBUG if (!other_it) BAD_PARAMETER.error ("ELIST2_ITERATOR::extract_sublist", ABORT, "other_it NULL"); if (!list) NO_LIST.error ("ELIST2_ITERATOR::extract_sublist", ABORT, NULL); if (list != other_it->list) BAD_EXTRACTION_PTS.error ("ELIST2_ITERATOR.extract_sublist", ABORT, NULL); if (list->empty ()) EMPTY_LIST.error ("ELIST2_ITERATOR::extract_sublist", ABORT, NULL); if (!current || !other_it->current) DONT_EXTRACT_DELETED.error ("ELIST2_ITERATOR.extract_sublist", ABORT, NULL); #endif ex_current_was_last = other_it->ex_current_was_last = FALSE; ex_current_was_cycle_pt = FALSE; other_it->ex_current_was_cycle_pt = FALSE; temp_it.mark_cycle_pt (); do { //walk sublist if (temp_it.cycled_list ()) //can't find end pt BAD_SUBLIST.error ("ELIST2_ITERATOR.extract_sublist", ABORT, NULL); if (temp_it.at_last ()) { list->last = prev; ex_current_was_last = other_it->ex_current_was_last = TRUE; } if (temp_it.current == cycle_pt) ex_current_was_cycle_pt = TRUE; if (temp_it.current == other_it->cycle_pt) other_it->ex_current_was_cycle_pt = TRUE; temp_it.forward (); } //do INCLUSIVE list while (temp_it.prev != other_it->current); //circularise sublist other_it->current->next = current; //circularise sublist current->prev = other_it->current; end_of_new_list = other_it->current; //sublist = whole list if (prev == other_it->current) { list->last = NULL; prev = current = next = NULL; other_it->prev = other_it->current = other_it->next = NULL; } else { prev->next = other_it->next; other_it->next->prev = prev; current = other_it->current = NULL; next = other_it->next; other_it->prev = prev; } return end_of_new_list; } tesseract-3.04.01/ccutil/elst2.h000066400000000000000000001002401266071204500163410ustar00rootroot00000000000000/********************************************************************** * File: elst2.h (Formerly elist2.h) * Description: Double linked embedded list module include file. * Author: Phil Cheatle * Created: Wed Jan 23 11:04:47 GMT 1991 * * (C) Copyright 1991, 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 ELST2_H #define ELST2_H #include #include "host.h" #include "serialis.h" #include "lsterr.h" class ELIST2_ITERATOR; /********************************************************************** DESIGN NOTE =========== It would probably be possible to implement the ELIST2 classes as derived classes from ELIST. I haven't done this because: a) I think it would be harder to understand the code (Though the problem with not inheriting is that changes to ELIST must be reflected in ELIST2 and vice versa) b) Most of the code is inline so: i) The duplication in source does not affect the run time code size - the code is copied inline anyway! ii) The compiler should have a bit less work to do! **********************************************************************/ /********************************************************************** * CLASS - ELIST2_LINK * * Generic link class for doubly linked lists with embedded links * * Note: No destructor - elements are assumed to be destroyed EITHER after * they have been extracted from a list OR by the ELIST2 destructor which * walks the list. **********************************************************************/ class DLLSYM ELIST2_LINK { friend class ELIST2_ITERATOR; friend class ELIST2; ELIST2_LINK *prev; ELIST2_LINK *next; public: ELIST2_LINK() { //constructor prev = next = NULL; } ELIST2_LINK( //copy constructor const ELIST2_LINK &) { //don't copy link prev = next = NULL; } void operator= ( //don't copy links const ELIST2_LINK &) { prev = next = NULL; } }; /********************************************************************** * CLASS - ELIST2 * * Generic list class for doubly linked lists with embedded links **********************************************************************/ class DLLSYM ELIST2 { friend class ELIST2_ITERATOR; ELIST2_LINK *last; //End of list //(Points to head) ELIST2_LINK *First() { // return first return last ? last->next : NULL; } public: ELIST2() { //constructor last = NULL; } void internal_clear ( //destroy all links void (*zapper) (ELIST2_LINK *)); //ptr to zapper functn bool empty() const { //is list empty? return !last; } bool singleton() const { return last ? (last == last->next) : false; } void shallow_copy( //dangerous!! ELIST2 *from_list) { //beware destructors!! last = from_list->last; } //ptr to copier functn void internal_deep_copy (ELIST2_LINK * (*copier) (ELIST2_LINK *), const ELIST2 * list); //list being copied void assign_to_sublist( //to this list ELIST2_ITERATOR *start_it, //from list start ELIST2_ITERATOR *end_it); //from list end inT32 length() const; // # elements in list void sort ( //sort elements int comparator ( //comparison routine const void *, const void *)); // Assuming list has been sorted already, insert new_link to // keep the list sorted according to the same comparison function. // Comparison function is the same as used by sort, i.e. uses double // indirection. Time is O(1) to add to beginning or end. // Time is linear to add pre-sorted items to an empty list. void add_sorted(int comparator(const void*, const void*), ELIST2_LINK* new_link); }; /*********************************************************************** * CLASS - ELIST2_ITERATOR * * Generic iterator class for doubly linked lists with embedded links **********************************************************************/ class DLLSYM ELIST2_ITERATOR { friend void ELIST2::assign_to_sublist(ELIST2_ITERATOR *, ELIST2_ITERATOR *); ELIST2 *list; //List being iterated ELIST2_LINK *prev; //prev element ELIST2_LINK *current; //current element ELIST2_LINK *next; //next element BOOL8 ex_current_was_last; //current extracted //was end of list BOOL8 ex_current_was_cycle_pt; //current extracted //was cycle point ELIST2_LINK *cycle_pt; //point we are cycling //the list to. BOOL8 started_cycling; //Have we moved off //the start? ELIST2_LINK *extract_sublist( //from this current... ELIST2_ITERATOR *other_it); //to other current public: ELIST2_ITERATOR() { //constructor list = NULL; } //unassigned list ELIST2_ITERATOR( //constructor ELIST2 *list_to_iterate); void set_to_list( //change list ELIST2 *list_to_iterate); void add_after_then_move( //add after current & ELIST2_LINK *new_link); //move to new void add_after_stay_put( //add after current & ELIST2_LINK *new_link); //stay at current void add_before_then_move( //add before current & ELIST2_LINK *new_link); //move to new void add_before_stay_put( //add before current & ELIST2_LINK *new_link); //stay at current void add_list_after( //add a list & ELIST2 *list_to_add); //stay at current void add_list_before( //add a list & ELIST2 *list_to_add); //move to it 1st item ELIST2_LINK *data() { //get current data #ifndef NDEBUG if (!current) NULL_DATA.error ("ELIST2_ITERATOR::data", ABORT, NULL); if (!list) NO_LIST.error ("ELIST2_ITERATOR::data", ABORT, NULL); #endif return current; } ELIST2_LINK *data_relative( //get data + or - ... inT8 offset); //offset from current ELIST2_LINK *forward(); //move to next element ELIST2_LINK *backward(); //move to prev element ELIST2_LINK *extract(); //remove from list //go to start of list ELIST2_LINK *move_to_first(); ELIST2_LINK *move_to_last(); //go to end of list void mark_cycle_pt(); //remember current BOOL8 empty() { //is list empty? #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::empty", ABORT, NULL); #endif return list->empty (); } BOOL8 current_extracted() { //current extracted? return !current; } BOOL8 at_first(); //Current is first? BOOL8 at_last(); //Current is last? BOOL8 cycled_list(); //Completed a cycle? void add_to_end( //add at end & ELIST2_LINK *new_link); //don't move void exchange( //positions of 2 links ELIST2_ITERATOR *other_it); //other iterator inT32 length(); //# elements in list void sort ( //sort elements int comparator ( //comparison routine const void *, const void *)); }; /*********************************************************************** * ELIST2_ITERATOR::set_to_list * * (Re-)initialise the iterator to point to the start of the list_to_iterate * over. **********************************************************************/ inline void ELIST2_ITERATOR::set_to_list( //change list ELIST2 *list_to_iterate) { #ifndef NDEBUG if (!list_to_iterate) BAD_PARAMETER.error ("ELIST2_ITERATOR::set_to_list", ABORT, "list_to_iterate is NULL"); #endif list = list_to_iterate; prev = list->last; current = list->First (); next = current ? current->next : NULL; cycle_pt = NULL; //await explicit set started_cycling = FALSE; ex_current_was_last = FALSE; ex_current_was_cycle_pt = FALSE; } /*********************************************************************** * ELIST2_ITERATOR::ELIST2_ITERATOR * * CONSTRUCTOR - set iterator to specified list; **********************************************************************/ inline ELIST2_ITERATOR::ELIST2_ITERATOR(ELIST2 *list_to_iterate) { set_to_list(list_to_iterate); } /*********************************************************************** * ELIST2_ITERATOR::add_after_then_move * * Add a new element to the list after the current element and move the * iterator to the new element. **********************************************************************/ inline void ELIST2_ITERATOR::add_after_then_move( // element to add ELIST2_LINK *new_element) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::add_after_then_move", ABORT, NULL); if (!new_element) BAD_PARAMETER.error ("ELIST2_ITERATOR::add_after_then_move", ABORT, "new_element is NULL"); if (new_element->next) STILL_LINKED.error ("ELIST2_ITERATOR::add_after_then_move", ABORT, NULL); #endif if (list->empty ()) { new_element->next = new_element; new_element->prev = new_element; list->last = new_element; prev = next = new_element; } else { new_element->next = next; next->prev = new_element; if (current) { //not extracted new_element->prev = current; current->next = new_element; prev = current; if (current == list->last) list->last = new_element; } else { //current extracted new_element->prev = prev; prev->next = new_element; if (ex_current_was_last) list->last = new_element; if (ex_current_was_cycle_pt) cycle_pt = new_element; } } current = new_element; } /*********************************************************************** * ELIST2_ITERATOR::add_after_stay_put * * Add a new element to the list after the current element but do not move * the iterator to the new element. **********************************************************************/ inline void ELIST2_ITERATOR::add_after_stay_put( // element to add ELIST2_LINK *new_element) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::add_after_stay_put", ABORT, NULL); if (!new_element) BAD_PARAMETER.error ("ELIST2_ITERATOR::add_after_stay_put", ABORT, "new_element is NULL"); if (new_element->next) STILL_LINKED.error ("ELIST2_ITERATOR::add_after_stay_put", ABORT, NULL); #endif if (list->empty ()) { new_element->next = new_element; new_element->prev = new_element; list->last = new_element; prev = next = new_element; ex_current_was_last = FALSE; current = NULL; } else { new_element->next = next; next->prev = new_element; if (current) { //not extracted new_element->prev = current; current->next = new_element; if (prev == current) prev = new_element; if (current == list->last) list->last = new_element; } else { //current extracted new_element->prev = prev; prev->next = new_element; if (ex_current_was_last) { list->last = new_element; ex_current_was_last = FALSE; } } next = new_element; } } /*********************************************************************** * ELIST2_ITERATOR::add_before_then_move * * Add a new element to the list before the current element and move the * iterator to the new element. **********************************************************************/ inline void ELIST2_ITERATOR::add_before_then_move( // element to add ELIST2_LINK *new_element) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::add_before_then_move", ABORT, NULL); if (!new_element) BAD_PARAMETER.error ("ELIST2_ITERATOR::add_before_then_move", ABORT, "new_element is NULL"); if (new_element->next) STILL_LINKED.error ("ELIST2_ITERATOR::add_before_then_move", ABORT, NULL); #endif if (list->empty ()) { new_element->next = new_element; new_element->prev = new_element; list->last = new_element; prev = next = new_element; } else { prev->next = new_element; new_element->prev = prev; if (current) { //not extracted new_element->next = current; current->prev = new_element; next = current; } else { //current extracted new_element->next = next; next->prev = new_element; if (ex_current_was_last) list->last = new_element; if (ex_current_was_cycle_pt) cycle_pt = new_element; } } current = new_element; } /*********************************************************************** * ELIST2_ITERATOR::add_before_stay_put * * Add a new element to the list before the current element but don't move the * iterator to the new element. **********************************************************************/ inline void ELIST2_ITERATOR::add_before_stay_put( // element to add ELIST2_LINK *new_element) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::add_before_stay_put", ABORT, NULL); if (!new_element) BAD_PARAMETER.error ("ELIST2_ITERATOR::add_before_stay_put", ABORT, "new_element is NULL"); if (new_element->next) STILL_LINKED.error ("ELIST2_ITERATOR::add_before_stay_put", ABORT, NULL); #endif if (list->empty ()) { new_element->next = new_element; new_element->prev = new_element; list->last = new_element; prev = next = new_element; ex_current_was_last = TRUE; current = NULL; } else { prev->next = new_element; new_element->prev = prev; if (current) { //not extracted new_element->next = current; current->prev = new_element; if (next == current) next = new_element; } else { //current extracted new_element->next = next; next->prev = new_element; if (ex_current_was_last) list->last = new_element; } prev = new_element; } } /*********************************************************************** * ELIST2_ITERATOR::add_list_after * * Insert another list to this list after the current element but don't move the * iterator. **********************************************************************/ inline void ELIST2_ITERATOR::add_list_after(ELIST2 *list_to_add) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::add_list_after", ABORT, NULL); if (!list_to_add) BAD_PARAMETER.error ("ELIST2_ITERATOR::add_list_after", ABORT, "list_to_add is NULL"); #endif if (!list_to_add->empty ()) { if (list->empty ()) { list->last = list_to_add->last; prev = list->last; next = list->First (); ex_current_was_last = TRUE; current = NULL; } else { if (current) { //not extracted current->next = list_to_add->First (); current->next->prev = current; if (current == list->last) list->last = list_to_add->last; list_to_add->last->next = next; next->prev = list_to_add->last; next = current->next; } else { //current extracted prev->next = list_to_add->First (); prev->next->prev = prev; if (ex_current_was_last) { list->last = list_to_add->last; ex_current_was_last = FALSE; } list_to_add->last->next = next; next->prev = list_to_add->last; next = prev->next; } } list_to_add->last = NULL; } } /*********************************************************************** * ELIST2_ITERATOR::add_list_before * * Insert another list to this list before the current element. Move the * iterator to the start of the inserted elements * iterator. **********************************************************************/ inline void ELIST2_ITERATOR::add_list_before(ELIST2 *list_to_add) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::add_list_before", ABORT, NULL); if (!list_to_add) BAD_PARAMETER.error ("ELIST2_ITERATOR::add_list_before", ABORT, "list_to_add is NULL"); #endif if (!list_to_add->empty ()) { if (list->empty ()) { list->last = list_to_add->last; prev = list->last; current = list->First (); next = current->next; ex_current_was_last = FALSE; } else { prev->next = list_to_add->First (); prev->next->prev = prev; if (current) { //not extracted list_to_add->last->next = current; current->prev = list_to_add->last; } else { //current extracted list_to_add->last->next = next; next->prev = list_to_add->last; if (ex_current_was_last) list->last = list_to_add->last; if (ex_current_was_cycle_pt) cycle_pt = prev->next; } current = prev->next; next = current->next; } list_to_add->last = NULL; } } /*********************************************************************** * ELIST2_ITERATOR::extract * * Do extraction by removing current from the list, returning it to the * caller, but NOT updating the iterator. (So that any calling loop can do * this.) The iterator's current points to NULL. If the extracted element * is to be deleted, this is the callers responsibility. **********************************************************************/ inline ELIST2_LINK *ELIST2_ITERATOR::extract() { ELIST2_LINK *extracted_link; #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::extract", ABORT, NULL); if (!current) //list empty or //element extracted NULL_CURRENT.error ("ELIST2_ITERATOR::extract", ABORT, NULL); #endif if (list->singleton()) { // Special case where we do need to change the iterator. prev = next = list->last = NULL; } else { prev->next = next; //remove from list next->prev = prev; if (current == list->last) { list->last = prev; ex_current_was_last = TRUE; } else { ex_current_was_last = FALSE; } } // Always set ex_current_was_cycle_pt so an add/forward will work in a loop. ex_current_was_cycle_pt = (current == cycle_pt) ? TRUE : FALSE; extracted_link = current; extracted_link->next = NULL; //for safety extracted_link->prev = NULL; //for safety current = NULL; return extracted_link; } /*********************************************************************** * ELIST2_ITERATOR::move_to_first() * * Move current so that it is set to the start of the list. * Return data just in case anyone wants it. **********************************************************************/ inline ELIST2_LINK *ELIST2_ITERATOR::move_to_first() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::move_to_first", ABORT, NULL); #endif current = list->First (); prev = list->last; next = current ? current->next : NULL; return current; } /*********************************************************************** * ELIST2_ITERATOR::move_to_last() * * Move current so that it is set to the end of the list. * Return data just in case anyone wants it. **********************************************************************/ inline ELIST2_LINK *ELIST2_ITERATOR::move_to_last() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::move_to_last", ABORT, NULL); #endif current = list->last; prev = current ? current->prev : NULL; next = current ? current->next : NULL; return current; } /*********************************************************************** * ELIST2_ITERATOR::mark_cycle_pt() * * Remember the current location so that we can tell whether we've returned * to this point later. * * If the current point is deleted either now, or in the future, the cycle * point will be set to the next item which is set to current. This could be * by a forward, add_after_then_move or add_after_then_move. **********************************************************************/ inline void ELIST2_ITERATOR::mark_cycle_pt() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::mark_cycle_pt", ABORT, NULL); #endif if (current) cycle_pt = current; else ex_current_was_cycle_pt = TRUE; started_cycling = FALSE; } /*********************************************************************** * ELIST2_ITERATOR::at_first() * * Are we at the start of the list? * **********************************************************************/ inline BOOL8 ELIST2_ITERATOR::at_first() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::at_first", ABORT, NULL); #endif //we're at a deleted return ((list->empty ()) || (current == list->First ()) || ((current == NULL) && (prev == list->last) && //NON-last pt between !ex_current_was_last)); //first and last } /*********************************************************************** * ELIST2_ITERATOR::at_last() * * Are we at the end of the list? * **********************************************************************/ inline BOOL8 ELIST2_ITERATOR::at_last() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::at_last", ABORT, NULL); #endif //we're at a deleted return ((list->empty ()) || (current == list->last) || ((current == NULL) && (prev == list->last) && //last point between ex_current_was_last)); //first and last } /*********************************************************************** * ELIST2_ITERATOR::cycled_list() * * Have we returned to the cycle_pt since it was set? * **********************************************************************/ inline BOOL8 ELIST2_ITERATOR::cycled_list() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::cycled_list", ABORT, NULL); #endif return ((list->empty ()) || ((current == cycle_pt) && started_cycling)); } /*********************************************************************** * ELIST2_ITERATOR::length() * * Return the length of the list * **********************************************************************/ inline inT32 ELIST2_ITERATOR::length() { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::length", ABORT, NULL); #endif return list->length (); } /*********************************************************************** * ELIST2_ITERATOR::sort() * * Sort the elements of the list, then reposition at the start. * **********************************************************************/ inline void ELIST2_ITERATOR::sort ( //sort elements int comparator ( //comparison routine const void *, const void *)) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::sort", ABORT, NULL); #endif list->sort (comparator); move_to_first(); } /*********************************************************************** * ELIST2_ITERATOR::add_to_end * * Add a new element to the end of the list without moving the iterator. * This is provided because a single linked list cannot move to the last as * the iterator couldn't set its prev pointer. Adding to the end is * essential for implementing queues. **********************************************************************/ inline void ELIST2_ITERATOR::add_to_end( // element to add ELIST2_LINK *new_element) { #ifndef NDEBUG if (!list) NO_LIST.error ("ELIST2_ITERATOR::add_to_end", ABORT, NULL); if (!new_element) BAD_PARAMETER.error ("ELIST2_ITERATOR::add_to_end", ABORT, "new_element is NULL"); if (new_element->next) STILL_LINKED.error ("ELIST2_ITERATOR::add_to_end", ABORT, NULL); #endif if (this->at_last ()) { this->add_after_stay_put (new_element); } else { if (this->at_first ()) { this->add_before_stay_put (new_element); list->last = new_element; } else { //Iteratr is elsewhere new_element->next = list->last->next; new_element->prev = list->last; list->last->next->prev = new_element; list->last->next = new_element; list->last = new_element; } } } /*********************************************************************** QUOTE_IT MACRO DEFINITION =========================== Replace with "". may be an arbitrary number of tokens ***********************************************************************/ #define QUOTE_IT( parm ) #parm /*********************************************************************** ELIST2IZE( CLASSNAME ) MACRO DEFINITION ====================================== CLASSNAME is assumed to be the name of a class which has a baseclass of ELIST2_LINK. NOTE: Because we don't use virtual functions in the list code, the list code will NOT work correctly for classes derived from this. The macro generates: - An element deletion function: CLASSNAME##_zapper - An E_LIST2 subclass: CLASSNAME##_LIST - An E_LIST2_ITERATOR subclass: CLASSNAME##_IT NOTE: Generated names are DELIBERATELY designed to clash with those for ELISTIZE but NOT with those for CLISTIZE and CLIST2IZE Two macros are provided: ELIST2IZE and ELIST2IZEH The ...IZEH macros just define the class names for use in .h files The ...IZE macros define the code use in .c files ***********************************************************************/ /*********************************************************************** ELIST2IZEH( CLASSNAME ) MACRO ELIST2IZEH is a concatenation of 3 fragments ELIST2IZEH_A, ELIST2IZEH_B and ELIST2IZEH_C. ***********************************************************************/ #define ELIST2IZEH_A( CLASSNAME ) \ \ extern DLLSYM void CLASSNAME##_zapper( /*delete a link*/ \ ELIST2_LINK* link); /*link to delete*/ #define ELIST2IZEH_B( CLASSNAME ) \ \ /*********************************************************************** \ * CLASS - CLASSNAME##_LIST \ * \ * List class for class CLASSNAME \ * \ **********************************************************************/ \ \ class DLLSYM CLASSNAME##_LIST : public ELIST2 \ { \ public: \ CLASSNAME##_LIST():ELIST2() {} \ /* constructor */ \ \ CLASSNAME##_LIST( /* don't construct */ \ const CLASSNAME##_LIST&) /*by initial assign*/\ { DONT_CONSTRUCT_LIST_BY_COPY.error( QUOTE_IT( CLASSNAME##_LIST ), \ ABORT, NULL ); } \ \ void clear() /* delete elements */\ { ELIST2::internal_clear( &CLASSNAME##_zapper ); } \ \ ~CLASSNAME##_LIST() /* destructor */ \ { clear(); } \ \ /* Become a deep copy of src_list*/ \ void deep_copy(const CLASSNAME##_LIST* src_list, \ CLASSNAME* (*copier)(const CLASSNAME*)); \ \ void operator=( /* prevent assign */ \ const CLASSNAME##_LIST&) \ { DONT_ASSIGN_LISTS.error( QUOTE_IT( CLASSNAME##_LIST ), \ ABORT, NULL ); } #define ELIST2IZEH_C( CLASSNAME ) \ }; \ \ \ \ /*********************************************************************** \ * CLASS - CLASSNAME##_IT \ * \ * Iterator class for class CLASSNAME##_LIST \ * \ * Note: We don't need to coerce pointers to member functions input \ * parameters as these are automatically converted to the type of the base \ * type. ("A ptr to a class may be converted to a pointer to a public base \ * class of that class") \ **********************************************************************/ \ \ class DLLSYM CLASSNAME##_IT : public ELIST2_ITERATOR \ { \ public: \ CLASSNAME##_IT():ELIST2_ITERATOR(){} \ \ CLASSNAME##_IT( \ CLASSNAME##_LIST* list):ELIST2_ITERATOR(list){} \ \ CLASSNAME* data() \ { return (CLASSNAME*) ELIST2_ITERATOR::data(); } \ \ CLASSNAME* data_relative( \ inT8 offset) \ { return (CLASSNAME*) ELIST2_ITERATOR::data_relative( offset ); } \ \ CLASSNAME* forward() \ { return (CLASSNAME*) ELIST2_ITERATOR::forward(); } \ \ CLASSNAME* backward() \ { return (CLASSNAME*) ELIST2_ITERATOR::backward(); } \ \ CLASSNAME* extract() \ { return (CLASSNAME*) ELIST2_ITERATOR::extract(); } \ \ CLASSNAME* move_to_first() \ { return (CLASSNAME*) ELIST2_ITERATOR::move_to_first(); } \ \ CLASSNAME* move_to_last() \ { return (CLASSNAME*) ELIST2_ITERATOR::move_to_last(); } \ }; #define ELIST2IZEH( CLASSNAME ) \ \ ELIST2IZEH_A( CLASSNAME ) \ \ ELIST2IZEH_B( CLASSNAME ) \ \ ELIST2IZEH_C( CLASSNAME ) /*********************************************************************** ELIST2IZE( CLASSNAME ) MACRO ***********************************************************************/ #define ELIST2IZE( CLASSNAME ) \ \ /*********************************************************************** \ * CLASSNAME##_zapper \ * \ * A function which can delete a CLASSNAME element. This is passed to the \ * generic clear list member function so that when a list is cleared the \ * elements on the list are properly destroyed from the base class, even \ * though we don't use a virtual destructor function. \ **********************************************************************/ \ \ DLLSYM void CLASSNAME##_zapper( /*delete a link*/ \ ELIST2_LINK* link) /*link to delete*/ \ { \ delete (CLASSNAME *) link; \ } \ \ /* Become a deep copy of src_list*/ \ void CLASSNAME##_LIST::deep_copy(const CLASSNAME##_LIST* src_list, \ CLASSNAME* (*copier)(const CLASSNAME*)) { \ \ CLASSNAME##_IT from_it(const_cast(src_list)); \ CLASSNAME##_IT to_it(this); \ \ for (from_it.mark_cycle_pt(); !from_it.cycled_list(); from_it.forward()) \ to_it.add_after_then_move((*copier)(from_it.data())); \ } #endif tesseract-3.04.01/ccutil/errcode.cpp000066400000000000000000000060721266071204500172760ustar00rootroot00000000000000/********************************************************************** * File: errcode.c (Formerly error.c) * Description: Generic error handler function * Author: Ray Smith * Created: Tue May 1 16:28:39 BST 1990 * * (C) Copyright 1989, 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. * **********************************************************************/ #include #include #include #include #ifdef __UNIX__ #include #endif #include "tprintf.h" #include "errcode.h" const ERRCODE BADERRACTION = "Illegal error action"; #define MAX_MSG 1024 /********************************************************************** * error * * Print an error message and continue, exit or abort according to action. * Makes use of error messages and numbers in a common place. * **********************************************************************/ void ERRCODE::error( // handle error const char *caller, // name of caller TessErrorLogCode action, // action to take const char *format, ... // special message ) const { va_list args; // variable args char msg[MAX_MSG]; char *msgptr = msg; if (caller != NULL) //name of caller msgptr += sprintf (msgptr, "%s:", caller); //actual message msgptr += sprintf (msgptr, "Error:%s", message); if (format != NULL) { msgptr += sprintf (msgptr, ":"); va_start(args, format); //variable list #ifdef _WIN32 //print remainder msgptr += _vsnprintf (msgptr, MAX_MSG - 2 - (msgptr - msg), format, args); msg[MAX_MSG - 2] = '\0'; //ensure termination strcat (msg, "\n"); #else //print remainder msgptr += vsprintf (msgptr, format, args); //no specific msgptr += sprintf (msgptr, "\n"); #endif va_end(args); } else //no specific msgptr += sprintf (msgptr, "\n"); // %s is needed here so msg is printed correctly! fprintf(stderr, "%s", msg); int* p = NULL; switch (action) { case DBG: case TESSLOG: return; //report only case TESSEXIT: //err_exit(); case ABORT: // Create a deliberate segv as the stack trace is more useful that way. if (!*p) abort(); default: BADERRACTION.error ("error", ABORT, NULL); } } tesseract-3.04.01/ccutil/errcode.h000066400000000000000000000100451266071204500167360ustar00rootroot00000000000000/********************************************************************** * File: errcode.h (Formerly error.h) * Description: Header file for generic error handler class * Author: Ray Smith * Created: Tue May 1 16:23:36 BST 1990 * * (C) Copyright 1990, 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 ERRCODE_H #define ERRCODE_H #include "host.h" /*Control parameters for error()*/ enum TessErrorLogCode { DBG = -1, /*log without alert */ TESSLOG = 0, /*alert user */ TESSEXIT = 1, /*exit after erro */ ABORT = 2 /*abort after error */ }; /* Explicit Error Abort codes */ #define NO_ABORT_CODE 0 #define LIST_ABORT 1 #define MEMORY_ABORT 2 #define FILE_ABORT 3 /* Location of code at error codes Reserve 0..2 (status codes 0..23 for UNLV)*/ #define LOC_UNUSED0 0 #define LOC_UNUSED1 1 #define LOC_UNUSED2 2 #define LOC_INIT 3 #define LOC_EDGE_PROG 4 #define LOC_TEXT_ORD_ROWS 5 #define LOC_TEXT_ORD_WORDS 6 #define LOC_PASS1 7 #define LOC_PASS2 8 /* Reserve up to 8..13 for adding subloc 0/3 plus subsubloc 0/1/2 */ #define LOC_FUZZY_SPACE 14 /* Reserve up to 14..20 for adding subloc 0/3 plus subsubloc 0/1/2 */ #define LOC_MM_ADAPT 21 #define LOC_DOC_BLK_REJ 22 #define LOC_WRITE_RESULTS 23 #define LOC_ADAPTIVE 24 /* DON'T DEFINE ANY LOCATION > 31 !!! */ /* Sub locatation determines whether pass2 was in normal mode or fix xht mode*/ #define SUBLOC_NORM 0 #define SUBLOC_FIX_XHT 3 /* Sub Sub locatation determines whether match_word_pass2 was in Tess matcher, NN matcher or somewhere else */ #define SUBSUBLOC_OTHER 0 #define SUBSUBLOC_TESS 1 #define SUBSUBLOC_NN 2 class TESS_API ERRCODE { // error handler class const char *message; // error message public: void error( // error print function const char *caller, // function location TessErrorLogCode action, // action to take const char *format, ... // fprintf format ) const; ERRCODE(const char *string) { message = string; } // initialize with string }; const ERRCODE ASSERT_FAILED = "Assert failed"; #define ASSERT_HOST(x) if (!(x)) \ { \ ASSERT_FAILED.error(#x, ABORT, "in file %s, line %d", \ __FILE__, __LINE__); \ } #ifdef _MSC_VER #define ASSERT_HOST_MSG(x, msg, ...) if (!(x)) \ { \ tprintf(msg); \ ASSERT_FAILED.error(#x, ABORT, "in file %s, line %d", \ __FILE__, __LINE__); \ } #else #define ASSERT_HOST_MSG(x, msg...) if (!(x)) \ { \ tprintf(msg); \ ASSERT_FAILED.error(#x, ABORT, "in file %s, line %d", \ __FILE__, __LINE__); \ } #endif void signal_exit(int signal_code); void set_global_loc_code(int loc_code); void set_global_subloc_code(int loc_code); void set_global_subsubloc_code(int loc_code); #endif tesseract-3.04.01/ccutil/fileerr.h000066400000000000000000000026331266071204500167470ustar00rootroot00000000000000/********************************************************************** * File: fileerr.h (Formerly filerr.h) * Description: Errors for file utilities. * Author: Ray Smith * Created: Tue Aug 14 15:45:16 BST 1990 * * (C) Copyright 1990, 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 FILEERR_H #define FILEERR_H #include "errcode.h" const ERRCODE CANTOPENFILE = "Can't open file"; const ERRCODE CANTCREATEFILE = "Can't create file"; const ERRCODE CANTMAKEPIPE = "Can't create pipe"; const ERRCODE CANTCONNECTPIPE = "Can't reconnect pipes to stdin/stdout"; const ERRCODE READFAILED = "Read of file failed"; const ERRCODE WRITEFAILED = "Write of file failed"; const ERRCODE SELECTFAILED = "Select failed"; const ERRCODE EXECFAILED = "Could not exec new process"; #endif tesseract-3.04.01/ccutil/genericheap.h000066400000000000000000000210661266071204500175720ustar00rootroot00000000000000// 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_; } // Provides read-only access to an element of the underlying vector. const Pair& get(int index) const { return heap_[index]; } // 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_ tesseract-3.04.01/ccutil/genericvector.h000066400000000000000000001035671266071204500201660ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: genericvector.h // Description: Generic vector class // Author: Daria Antonova // Created: Mon Jun 23 11:26:43 PDT 2008 // // (C) Copyright 2007, 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_GENERICVECTOR_H_ #define TESSERACT_CCUTIL_GENERICVECTOR_H_ #include #include #include #include "tesscallback.h" #include "errcode.h" #include "helpers.h" #include "ndminx.h" #include "serialis.h" #include "strngs.h" // Use PointerVector below in preference to GenericVector, as that // provides automatic deletion of pointers, [De]Serialize that works, and // sort that works. template class GenericVector { public: GenericVector() { init(kDefaultVectorSize); } GenericVector(int size, T init_val) { init(size); init_to_size(size, init_val); } // Copy GenericVector(const GenericVector& other) { this->init(other.size()); this->operator+=(other); } GenericVector &operator+=(const GenericVector& other); GenericVector &operator=(const GenericVector& other); ~GenericVector(); // Reserve some memory. void reserve(int size); // Double the size of the internal array. void double_the_size(); // Resizes to size and sets all values to t. void init_to_size(int size, T t); // Resizes to size without any initialization. void resize_no_init(int size) { reserve(size); size_used_ = size; } // Return the size used. int size() const { return size_used_; } int size_reserved() const { return size_reserved_; } int length() const { return size_used_; } // Return true if empty. bool empty() const { return size_used_ == 0; } // Return the object from an index. 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 // set_compare_callback. int get_index(T object) const; // Return true if T is in the array bool contains(T object) const; // Return true if the index is valid T contains_index(int index) const; // Push an element in the end of the array int push_back(T object); void operator+=(T t); // Push an element in the end of the array if the same // element is not already contained in the array. int push_back_new(T object); // Push an element in the front of the array // Note: This function is O(n) int push_front(T object); // Set the value at the given index void set(T t, int index); // Insert t at the given index, push other elements to the right. void insert(T t, int index); // Removes an element at the given index and // shifts the remaining elements to the left. 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. void truncate(int size) { if (size < size_used_) size_used_ = size; } // Add a callback to be called to delete the elements when the array took // their ownership. void set_clear_callback(TessCallback1* cb); // Add a callback to be called to compare the elements when needed (contains, // get_id, ...) void set_compare_callback(TessResultCallback2* cb); // Clear the array, calling the clear callback function if any. // 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. void clear(); // Delete objects pointed to by data_[i] void delete_data_pointers(); // This method clears the current object, then, does a shallow copy of // its argument, and finally invalidates its argument. // Callbacks are moved to the current object; void move(GenericVector* from); // Read/Write the array to a file. This does _NOT_ read/write the callbacks. // The callback given must be permanent since they will be called more than // once. The given callback will be deleted at the end. // If the callbacks are NULL, then the data is simply read/written using // fread (and swapping)/fwrite. // Returns false on error or if the callback returns false. // DEPRECATED. Use [De]Serialize[Classes] instead. bool write(FILE* f, TessResultCallback2* cb) const; 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. // TODO(rays) Change all callers to use TFile and remove deprecated methods. bool Serialize(FILE* fp) const; bool Serialize(tesseract::TFile* 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. bool DeSerialize(bool swap, FILE* fp); bool DeSerialize(bool swap, tesseract::TFile* 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. bool SerializeClasses(FILE* fp) const; bool SerializeClasses(tesseract::TFile* fp) const; // Reads a vector of classes from the given file. Assumes the existence of // bool T::Deserialize(bool swap, FILE* fp) that returns false in case of // error. Also needs T::T() and T::T(constT&), as init_to_size is used in // this function. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerializeClasses(bool swap, FILE* fp); bool DeSerializeClasses(bool swap, tesseract::TFile* fp); // Allocates a new array of double the current_size, copies over the // information from data to the new location, deletes data and returns // the pointed to the new larger array. // This function uses memcpy to copy the data, instead of invoking // operator=() for each element like double_the_size() does. static T *double_the_size_memcpy(int current_size, T *data) { T *data_new = new T[current_size * 2]; memcpy(data_new, data, sizeof(T) * current_size); delete[] data; return data_new; } // Reverses the elements of the vector. void reverse() { for (int i = 0; i < size_used_ / 2; ++i) Swap(&data_[i], &data_[size_used_ - 1 - i]); } // Sorts the members of this vector using the less than comparator (cmp_lt), // which compares the values. Useful for GenericVectors to primitive types. // Will not work so great for pointers (unless you just want to sort some // pointers). You need to provide a specialization to sort_cmp to use // your type. void sort(); // Sort the array into the order defined by the qsort function comparator. // The comparator function is as defined by qsort, ie. it receives pointers // to two Ts and returns negative if the first element is to appear earlier // in the result and positive if it is to appear later, with 0 for equal. void sort(int (*comparator)(const void*, const void*)) { qsort(data_, size_used_, sizeof(*data_), comparator); } // Searches the array (assuming sorted in ascending order, using sort()) for // an element equal to target and returns true if it is present. // Use binary_search to get the index of target, or its nearest candidate. bool bool_binary_search(const T& target) const { int index = binary_search(target); if (index >= size_used_) return false; return data_[index] == target; } // Searches the array (assuming sorted in ascending order, using sort()) for // an element equal to target and returns the index of the best candidate. // The return value is conceptually the largest index i such that // data_[i] <= target or 0 if target < the whole vector. // NOTE that this function uses operator> so really the return value is // the largest index i such that data_[i] > target is false. int binary_search(const T& target) const { int bottom = 0; int top = size_used_; do { int middle = (bottom + top) / 2; if (data_[middle] > target) top = middle; else bottom = middle; } while (top - bottom > 1); return bottom; } // Compact the vector by deleting elements using operator!= on basic types. // The vector must be sorted. void compact_sorted() { if (size_used_ == 0) return; // First element is in no matter what, hence the i = 1. int last_write = 0; for (int i = 1; i < size_used_; ++i) { // Finds next unique item and writes it. if (data_[last_write] != data_[i]) data_[++last_write] = data_[i]; } // last_write is the index of a valid data cell, so add 1. size_used_ = last_write + 1; } // Compact the vector by deleting elements for which delete_cb returns // true. delete_cb is a permanent callback and will be deleted. void compact(TessResultCallback1* delete_cb) { int new_size = 0; int old_index = 0; // Until the callback returns true, the elements stay the same. while (old_index < size_used_ && !delete_cb->Run(old_index++)) ++new_size; // Now just copy anything else that gets false from delete_cb. for (; old_index < size_used_; ++old_index) { if (!delete_cb->Run(old_index)) { data_[new_size++] = data_[old_index]; } } size_used_ = new_size; delete delete_cb; } T dot_product(const GenericVector& other) const { T result = static_cast(0); for (int i = MIN(size_used_, other.size_used_) - 1; i >= 0; --i) result += data_[i] * other.data_[i]; 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; } } // Returns true if all elements of *this are within the given range. // Only uses operator< bool WithinBounds(const T& rangemin, const T& rangemax) const { for (int i = 0; i < size_used_; ++i) { if (data_[i] < rangemin || rangemax < data_[i]) return false; } return true; } 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); // We are assuming that the object generally placed in thie // vector are small enough that for efficiency it makes sense // to start with a larger initial size. static const int kDefaultVectorSize = 4; inT32 size_used_; inT32 size_reserved_; T* data_; TessCallback1* clear_cb_; // Mutable because Run method is not const mutable TessResultCallback2* compare_cb_; }; namespace tesseract { // Function to read a GenericVector from a whole file. // Returns false on failure. typedef bool (*FileReader)(const STRING& filename, GenericVector* data); // Function to write a GenericVector to a whole file. // Returns false on failure. typedef bool (*FileWriter)(const GenericVector& data, const STRING& filename); // The default FileReader loads the whole file into the vector of char, // returning false on error. inline bool LoadDataFromFile(const STRING& filename, GenericVector* data) { FILE* fp = fopen(filename.string(), "rb"); if (fp == NULL) return false; fseek(fp, 0, SEEK_END); size_t size = ftell(fp); fseek(fp, 0, SEEK_SET); // Pad with a 0, just in case we treat the result as a string. data->init_to_size((int)size + 1, 0); bool result = fread(&(*data)[0], 1, size, fp) == size; fclose(fp); return result; } // The default FileWriter writes the vector of char to the filename file, // returning false on error. inline bool SaveDataToFile(const GenericVector& data, const STRING& filename) { FILE* fp = fopen(filename.string(), "wb"); if (fp == NULL) return false; bool result = static_cast(fwrite(&data[0], 1, data.size(), fp)) == data.size(); fclose(fp); return result; } template bool cmp_eq(T const & t1, T const & t2) { return t1 == t2; } // Used by sort() // return < 0 if t1 < t2 // return 0 if t1 == t2 // return > 0 if t1 > t2 template int sort_cmp(const void* t1, const void* t2) { const T* a = static_cast (t1); const T* b = static_cast (t2); if (*a < *b) { return -1; } else if (*b < *a) { return 1; } else { return 0; } } // Used by PointerVector::sort() // return < 0 if t1 < t2 // return 0 if t1 == t2 // return > 0 if t1 > t2 template int sort_ptr_cmp(const void* t1, const void* t2) { const T* a = *reinterpret_cast(t1); const T* b = *reinterpret_cast(t2); if (*a < *b) { return -1; } else if (*b < *a) { return 1; } else { return 0; } } // Subclass for a vector of pointers. Use in preference to GenericVector // as it provides automatic deletion and correct serialization, with the // corollary that all copy operations are deep copies of the pointed-to objects. template class PointerVector : public GenericVector { public: PointerVector() : GenericVector() { } explicit PointerVector(int size) : GenericVector(size) { } ~PointerVector() { // Clear must be called here, even though it is called again by the base, // as the base will call the wrong clear. clear(); } // Copy must be deep, as the pointers will be automatically deleted on // destruction. PointerVector(const PointerVector& other) : GenericVector(other) { this->init(other.size()); this->operator+=(other); } PointerVector& operator+=(const PointerVector& other) { this->reserve(this->size_used_ + other.size_used_); for (int i = 0; i < other.size(); ++i) { this->push_back(new T(*other.data_[i])); } return *this; } PointerVector& operator=(const PointerVector& other) { if (&other != this) { this->truncate(0); this->operator+=(other); } return *this; } // Removes an element at the given index and // shifts the remaining elements to the left. 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. void truncate(int size) { for (int i = size; i < GenericVector::size_used_; ++i) delete GenericVector::data_[i]; GenericVector::truncate(size); } // Compact the vector by deleting elements for which delete_cb returns // true. delete_cb is a permanent callback and will be deleted. void compact(TessResultCallback1* delete_cb) { int new_size = 0; int old_index = 0; // Until the callback returns true, the elements stay the same. while (old_index < GenericVector::size_used_ && !delete_cb->Run(GenericVector::data_[old_index++])) ++new_size; // Now just copy anything else that gets false from delete_cb. for (; old_index < GenericVector::size_used_; ++old_index) { if (!delete_cb->Run(GenericVector::data_[old_index])) { GenericVector::data_[new_size++] = GenericVector::data_[old_index]; } else { delete GenericVector::data_[old_index]; } } GenericVector::size_used_ = new_size; delete delete_cb; } // Clear the array, calling the clear callback function if any. // 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. void clear() { GenericVector::delete_data_pointers(); GenericVector::clear(); } // Writes a vector of (pointers to) classes to the given file. Assumes the // existence of bool T::Serialize(FILE*) const that returns false in case of // error. There is no Serialize for simple types, as you would have a // normal GenericVector of those. // Returns false in case of error. 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) { inT8 non_null = GenericVector::data_[i] != NULL; if (fwrite(&non_null, sizeof(non_null), 1, fp) != 1) return false; if (non_null && !GenericVector::data_[i]->Serialize(fp)) return false; } return true; } bool Serialize(TFile* fp) const { inT32 used = GenericVector::size_used_; if (fp->FWrite(&used, sizeof(used), 1) != 1) return false; for (int i = 0; i < used; ++i) { inT8 non_null = GenericVector::data_[i] != NULL; if (fp->FWrite(&non_null, sizeof(non_null), 1) != 1) return false; if (non_null && !GenericVector::data_[i]->Serialize(fp)) return false; } return true; } // Reads a vector of (pointers to) classes to the given file. Assumes the // existence of bool T::DeSerialize(bool, Tfile*) const that returns false in // case of error. There is no Serialize for simple types, as you would have a // normal GenericVector of those. // If swap is true, assumes a big/little-endian swap is needed. // Also needs T::T(), as new T is used in this function. // Returns false in case of error. bool DeSerialize(bool swap, FILE* fp) { inT32 reserved; if (fread(&reserved, sizeof(reserved), 1, fp) != 1) return false; if (swap) Reverse32(&reserved); GenericVector::reserve(reserved); truncate(0); for (int i = 0; i < reserved; ++i) { inT8 non_null; if (fread(&non_null, sizeof(non_null), 1, fp) != 1) return false; T* item = NULL; if (non_null) { item = new T; if (!item->DeSerialize(swap, fp)) { delete item; return false; } this->push_back(item); } else { // Null elements should keep their place in the vector. this->push_back(NULL); } } return true; } bool DeSerialize(bool swap, TFile* fp) { inT32 reserved; if (fp->FRead(&reserved, sizeof(reserved), 1) != 1) return false; if (swap) Reverse32(&reserved); GenericVector::reserve(reserved); truncate(0); for (int i = 0; i < reserved; ++i) { inT8 non_null; if (fp->FRead(&non_null, sizeof(non_null), 1) != 1) return false; T* item = NULL; if (non_null) { item = new T; if (!item->DeSerialize(swap, fp)) { delete item; return false; } this->push_back(item); } else { // Null elements should keep their place in the vector. this->push_back(NULL); } } return true; } // Sorts the items pointed to by the members of this vector using // t::operator<(). void sort() { sort(&sort_ptr_cmp); } }; } // namespace tesseract // A useful vector that uses operator== to do comparisons. template class GenericVectorEqEq : public GenericVector { public: GenericVectorEqEq() { GenericVector::set_compare_callback( NewPermanentTessCallback(tesseract::cmp_eq)); } GenericVectorEqEq(int size) : GenericVector(size) { GenericVector::set_compare_callback( NewPermanentTessCallback(tesseract::cmp_eq)); } }; template void GenericVector::init(int size) { size_used_ = 0; size_reserved_ = 0; data_ = 0; clear_cb_ = 0; compare_cb_ = 0; reserve(size); } template GenericVector::~GenericVector() { clear(); } // Reserve some memory. If the internal array contains elements, they are // copied. template void GenericVector::reserve(int size) { if (size_reserved_ >= size || size <= 0) return; T* new_array = new T[size]; for (int i = 0; i < size_used_; ++i) new_array[i] = data_[i]; if (data_ != NULL) delete[] data_; data_ = new_array; size_reserved_ = size; } template void GenericVector::double_the_size() { if (size_reserved_ == 0) { reserve(kDefaultVectorSize); } else { reserve(2 * size_reserved_); } } // Resizes to size and sets all values to t. template void GenericVector::init_to_size(int size, T t) { reserve(size); size_used_ = size; for (int i = 0; i < size; ++i) data_[i] = t; } // Return the object from an index. template T &GenericVector::get(int index) const { ASSERT_HOST(index >= 0 && index < size_used_); return data_[index]; } template T &GenericVector::operator[](int index) const { assert(index >= 0 && index < size_used_); return data_[index]; } template 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 void GenericVector::set(T t, int index) { ASSERT_HOST(index >= 0 && index < size_used_); data_[index] = t; } // Shifts the rest of the elements to the right to make // space for the new elements and inserts the given element // at the specified index. template void GenericVector::insert(T t, int index) { ASSERT_HOST(index >= 0 && index <= size_used_); if (size_reserved_ == size_used_) double_the_size(); for (int i = size_used_; i > index; --i) { data_[i] = data_[i-1]; } data_[index] = t; size_used_++; } // Removes an element at the given index and // shifts the remaining elements to the left. template void GenericVector::remove(int index) { ASSERT_HOST(index >= 0 && index < size_used_); for (int i = index; i < size_used_ - 1; ++i) { data_[i] = data_[i+1]; } size_used_--; } // Return true if the index is valindex template T GenericVector::contains_index(int index) const { return index >= 0 && index < size_used_; } // Return the index of the T object. template int GenericVector::get_index(T object) const { for (int i = 0; i < size_used_; ++i) { ASSERT_HOST(compare_cb_ != NULL); if (compare_cb_->Run(object, data_[i])) return i; } return -1; } // Return true if T is in the array template bool GenericVector::contains(T object) const { return get_index(object) != -1; } // Add an element in the array template int GenericVector::push_back(T object) { int index = 0; if (size_used_ == size_reserved_) double_the_size(); index = size_used_++; data_[index] = object; return index; } template int GenericVector::push_back_new(T object) { int index = get_index(object); if (index >= 0) return index; return push_back(object); } // Add an element in the array (front) template int GenericVector::push_front(T object) { if (size_used_ == size_reserved_) double_the_size(); for (int i = size_used_; i > 0; --i) data_[i] = data_[i-1]; data_[0] = object; ++size_used_; return 0; } template void GenericVector::operator+=(T t) { push_back(t); } template GenericVector &GenericVector::operator+=(const GenericVector& other) { this->reserve(size_used_ + other.size_used_); for (int i = 0; i < other.size(); ++i) { this->operator+=(other.data_[i]); } return *this; } template GenericVector &GenericVector::operator=(const GenericVector& other) { if (&other != this) { this->truncate(0); this->operator+=(other); } return *this; } // Add a callback to be called to delete the elements when the array took // their ownership. template void GenericVector::set_clear_callback(TessCallback1* cb) { clear_cb_ = 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) { compare_cb_ = cb; } // Clear the array, calling the callback function if any. template void GenericVector::clear() { if (size_reserved_ > 0) { if (clear_cb_ != NULL) for (int i = 0; i < size_used_; ++i) clear_cb_->Run(data_[i]); delete[] data_; data_ = NULL; size_used_ = 0; size_reserved_ = 0; } if (clear_cb_ != NULL) { delete clear_cb_; clear_cb_ = NULL; } if (compare_cb_ != NULL) { delete compare_cb_; compare_cb_ = NULL; } } template void GenericVector::delete_data_pointers() { for (int i = 0; i < size_used_; ++i) if (data_[i]) { delete data_[i]; } } template bool GenericVector::write( FILE* f, TessResultCallback2* cb) const { if (fwrite(&size_reserved_, sizeof(size_reserved_), 1, f) != 1) return false; if (fwrite(&size_used_, sizeof(size_used_), 1, f) != 1) return false; if (cb != NULL) { for (int i = 0; i < size_used_; ++i) { if (!cb->Run(f, data_[i])) { delete cb; return false; } } delete cb; } else { if (fwrite(data_, sizeof(T), size_used_, f) != size_used_) return false; } return true; } template bool GenericVector::read(FILE* f, TessResultCallback3* cb, bool swap) { inT32 reserved; if (fread(&reserved, sizeof(reserved), 1, f) != 1) return false; if (swap) Reverse32(&reserved); reserve(reserved); if (fread(&size_used_, sizeof(size_used_), 1, f) != 1) return false; if (swap) Reverse32(&size_used_); if (cb != NULL) { for (int i = 0; i < size_used_; ++i) { if (!cb->Run(f, data_ + i, swap)) { delete cb; return false; } } delete cb; } else { if (fread(data_, sizeof(T), size_used_, f) != size_used_) return false; if (swap) { for (int i = 0; i < size_used_; ++i) ReverseN(&data_[i], sizeof(T)); } } return true; } // 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. template bool GenericVector::Serialize(FILE* fp) const { if (fwrite(&size_used_, sizeof(size_used_), 1, fp) != 1) return false; if (fwrite(data_, sizeof(*data_), size_used_, fp) != size_used_) return false; return true; } template bool GenericVector::Serialize(tesseract::TFile* fp) const { if (fp->FWrite(&size_used_, sizeof(size_used_), 1) != 1) return false; if (fp->FWrite(data_, sizeof(*data_), size_used_) != size_used_) return false; return true; } // 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. template bool GenericVector::DeSerialize(bool swap, FILE* fp) { inT32 reserved; if (fread(&reserved, sizeof(reserved), 1, fp) != 1) return false; if (swap) Reverse32(&reserved); reserve(reserved); size_used_ = reserved; if (fread(data_, sizeof(T), size_used_, fp) != size_used_) return false; if (swap) { for (int i = 0; i < size_used_; ++i) ReverseN(&data_[i], sizeof(data_[i])); } return true; } template bool GenericVector::DeSerialize(bool swap, tesseract::TFile* fp) { inT32 reserved; if (fp->FRead(&reserved, sizeof(reserved), 1) != 1) return false; if (swap) Reverse32(&reserved); reserve(reserved); size_used_ = reserved; if (fp->FRead(data_, sizeof(T), size_used_) != size_used_) return false; if (swap) { for (int i = 0; i < size_used_; ++i) ReverseN(&data_[i], sizeof(data_[i])); } return true; } // 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. template bool GenericVector::SerializeClasses(FILE* fp) const { if (fwrite(&size_used_, sizeof(size_used_), 1, fp) != 1) return false; for (int i = 0; i < size_used_; ++i) { if (!data_[i].Serialize(fp)) return false; } return true; } template bool GenericVector::SerializeClasses(tesseract::TFile* fp) const { if (fp->FWrite(&size_used_, sizeof(size_used_), 1) != 1) return false; for (int i = 0; i < size_used_; ++i) { if (!data_[i].Serialize(fp)) return false; } return true; } // Reads a vector of classes from the given file. Assumes the existence of // bool T::Deserialize(bool swap, FILE* fp) that returns false in case of // error. Also needs T::T() and T::T(constT&), as init_to_size is used in // this function. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. template bool GenericVector::DeSerializeClasses(bool swap, FILE* fp) { uinT32 reserved; if (fread(&reserved, sizeof(reserved), 1, fp) != 1) return false; if (swap) Reverse32(&reserved); T empty; init_to_size(reserved, empty); for (int i = 0; i < reserved; ++i) { if (!data_[i].DeSerialize(swap, fp)) return false; } return true; } template bool GenericVector::DeSerializeClasses(bool swap, tesseract::TFile* fp) { uinT32 reserved; if (fp->FRead(&reserved, sizeof(reserved), 1) != 1) return false; if (swap) Reverse32(&reserved); T empty; init_to_size(reserved, empty); for (int i = 0; i < reserved; ++i) { if (!data_[i].DeSerialize(swap, fp)) return false; } return true; } // This method clear the current object, then, does a shallow copy of // its argument, and finally invalidates its argument. template void GenericVector::move(GenericVector* from) { this->clear(); this->data_ = from->data_; this->size_reserved_ = from->size_reserved_; this->size_used_ = from->size_used_; this->compare_cb_ = from->compare_cb_; this->clear_cb_ = from->clear_cb_; from->data_ = NULL; from->clear_cb_ = NULL; from->compare_cb_ = NULL; from->size_used_ = 0; from->size_reserved_ = 0; } template 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. #ifndef rand_r // _MSC_VER, ANDROID srand(*seed); #define rand_r(seed) rand() #endif // _MSC_VER 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_ tesseract-3.04.01/ccutil/globaloc.cpp000066400000000000000000000053731266071204500174400ustar00rootroot00000000000000/********************************************************************** * File: errcode.c (Formerly error.c) * Description: Generic error handler function * Author: Ray Smith * Created: Tue May 1 16:28:39 BST 1990 * * (C) Copyright 1989, 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. * **********************************************************************/ #include #ifdef __linux__ #include // For SYS_gettid. #include // For syscall itself. #endif #include "allheaders.h" #include "errcode.h" #include "tprintf.h" // Size of thread-id array of pixes to keep in case of crash. const int kMaxNumThreadPixes = 32768; Pix* global_crash_pixes[kMaxNumThreadPixes]; void SavePixForCrash(int resolution, Pix* pix) { #ifdef __linux__ #ifndef ANDROID int thread_id = syscall(SYS_gettid) % kMaxNumThreadPixes; #else int thread_id = gettid() % kMaxNumThreadPixes; #endif 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; } #endif } // 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__ #ifndef ANDROID int thread_id = syscall(SYS_gettid) % kMaxNumThreadPixes; #else int thread_id = gettid() % kMaxNumThreadPixes; #endif 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 } void err_exit() { ASSERT_HOST("Fatal error encountered!" == NULL); } void set_global_loc_code(int loc_code) { // global_loc_code = loc_code; } void set_global_subloc_code(int loc_code) { // global_subloc_code = loc_code; } void set_global_subsubloc_code(int loc_code) { // global_subsubloc_code = loc_code; } tesseract-3.04.01/ccutil/globaloc.h000066400000000000000000000026021266071204500170750ustar00rootroot00000000000000/********************************************************************** * File: errcode.h (Formerly error.h) * Description: Header file for generic error handler class * Author: Ray Smith * Created: Tue May 1 16:23:36 BST 1990 * * (C) Copyright 1990, 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 GLOBALOC_H #define GLOBALOC_H #include "host.h" // 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(); void set_global_loc_code(int loc_code); void set_global_subloc_code(int loc_code); void set_global_subsubloc_code(int loc_code); #endif tesseract-3.04.01/ccutil/hashfn.h000066400000000000000000000051251266071204500165650ustar00rootroot00000000000000/********************************************************************** * File: hashfn.h (Formerly hash.h) * Description: Portability hacks for hash_map, hash_set and unique_ptr. * Author: Ray Smith * Created: Wed Jan 08 14:08:25 PST 2014 * * (C) Copyright 2014, 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 HASHFN_H #define HASHFN_H #ifdef USE_STD_NAMESPACE #if (__cplusplus >= 201103L) || defined(_MSC_VER) // Visual Studio #include #include #define hash_map std::unordered_map #if (_MSC_VER >= 1500 && _MSC_VER < 1600) // Visual Studio 2008 using namespace std::tr1; #else // _MSC_VER using std::unordered_map; using std::unordered_set; #include #define SmartPtr std::unique_ptr #define HAVE_UNIQUE_PTR #endif // _MSC_VER #elif (defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ > 0)) || \ __GNUC__ >= 4)) // gcc // hash_set is deprecated in gcc #include #include using __gnu_cxx::hash_map; using __gnu_cxx::hash_set; #define unordered_map hash_map #define unordered_set hash_set #else #include #include #endif // gcc #elif (__clang__) #include #include #define hash_map std::unordered_map #define unordered_set std::unordered_set #else // USE_STD_NAMESPACE #include #include #define unordered_map hash_map #define unordered_set hash_set #endif // USE_STD_NAMESPACE #ifndef HAVE_UNIQUE_PTR // Trivial smart ptr. Expand to add features of std::unique_ptr as required. template class SmartPtr { public: SmartPtr() : ptr_(NULL) {} explicit SmartPtr(T* ptr) : ptr_(ptr) {} ~SmartPtr() { delete ptr_; } T* get() const { return ptr_; } void reset(T* ptr) { if (ptr_ != NULL) delete ptr_; ptr_ = ptr; } bool operator==(const T* ptr) const { return ptr_ == ptr; } T* operator->() const { return ptr_; } private: T* ptr_; }; #endif // HAVE_UNIQUE_PTR #endif // HASHFN_H tesseract-3.04.01/ccutil/helpers.h000066400000000000000000000141011266071204500167520ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: helpers.h * Description: General utility functions * Author: Daria Antonova * Created: Wed Apr 8 14:37:00 2009 * Language: C++ * Package: N/A * Status: Reusable Software Component * * (c) Copyright 2009, 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_HELPERS_H_ #define TESSERACT_CCUTIL_HELPERS_H_ #include #include #include "host.h" // TODO(rays) Put the rest of the helpers in the namespace. namespace tesseract { // A simple linear congruential random number generator, using Knuth's // constants from: // http://en.wikipedia.org/wiki/Linear_congruential_generator. class TRand { public: TRand() : seed_(1) {} // Sets the seed to the given value. void set_seed(uinT64 seed) { seed_ = seed; } // Returns an integer in the range 0 to MAX_INT32. inT32 IntRand() { Iterate(); return seed_ >> 33; } // Returns a floating point value in the range [-range, range]. double SignedRand(double range) { return range * 2.0 * IntRand() / MAX_INT32 - range; } // Returns a floating point value in the range [0, range]. double UnsignedRand(double range) { return range * IntRand() / MAX_INT32; } private: // Steps the generator to the next value. void Iterate() { seed_ *= 6364136223846793005ULL; seed_ += 1442695040888963407ULL; } // The current value of the seed. uinT64 seed_; }; } // namespace tesseract // Remove newline (if any) at the end of the string. inline void chomp_string(char *str) { int last_index = (int)strlen(str) - 1; while (last_index >= 0 && (str[last_index] == '\n' || str[last_index] == '\r')) { str[last_index--] = '\0'; } } // Advance the current pointer of the file if it points to a newline character. inline void SkipNewline(FILE *file) { if (fgetc(file) != '\n') fseek(file, -1, SEEK_CUR); } // Swaps the two args pointed to by the pointers. // Operator= and copy constructor must work on T. template inline void Swap(T* p1, T* p2) { T tmp(*p2); *p2 = *p1; *p1 = tmp; } // qsort function to sort 2 floats. inline int sort_floats(const void *arg1, const void *arg2) { float diff = *((float *) arg1) - *((float *) arg2); if (diff > 0) { return 1; } else if (diff < 0) { return -1; } else { return 0; } } // return the smallest multiple of block_size greater than or equal to n. inline int RoundUp(int n, int block_size) { return block_size * ((n + block_size - 1) / block_size); } // Clip a numeric value to the interval [lower_bound, upper_bound]. template inline T ClipToRange(const T& x, const T& lower_bound, const T& upper_bound) { if (x < lower_bound) return lower_bound; if (x > upper_bound) return upper_bound; return x; } // Extend the range [lower_bound, upper_bound] to include x. template inline void UpdateRange(const T1& x, T2* lower_bound, T2* upper_bound) { if (x < *lower_bound) *lower_bound = x; if (x > *upper_bound) *upper_bound = x; } // Decrease lower_bound to be <= x_lo AND increase upper_bound to be >= x_hi. template inline void UpdateRange(const T1& x_lo, const T1& x_hi, T2* lower_bound, T2* upper_bound) { if (x_lo < *lower_bound) *lower_bound = x_lo; if (x_hi > *upper_bound) *upper_bound = x_hi; } // Intersect the range [*lower2, *upper2] with the range [lower1, upper1], // putting the result back in [*lower2, *upper2]. // If non-intersecting ranges are given, we end up with *lower2 > *upper2. template inline void IntersectRange(const T& lower1, const T& upper1, T* lower2, T* upper2) { if (lower1 > *lower2) *lower2 = lower1; if (upper1 < *upper2) *upper2 = upper1; } // Proper modulo arithmetic operator. Returns a mod b that works for -ve a. // For any integer a and positive b, returns r : 0<=r= 0 ? (a + b / 2) / b : (a - b / 2) / b; } // Return a double cast to int with rounding. inline int IntCastRounded(double x) { return x >= 0.0 ? static_cast(x + 0.5) : -static_cast(-x + 0.5); } // Reverse the order of bytes in a n byte quantity for big/little-endian switch. inline void ReverseN(void* ptr, int num_bytes) { char *cptr = reinterpret_cast(ptr); int halfsize = num_bytes / 2; for (int i = 0; i < halfsize; ++i) { char tmp = cptr[i]; cptr[i] = cptr[num_bytes - 1 - i]; cptr[num_bytes - 1 - i] = tmp; } } // Reverse the order of bytes in a 16 bit quantity for big/little-endian switch. inline void Reverse16(void *ptr) { ReverseN(ptr, 2); } // Reverse the order of bytes in a 32 bit quantity for big/little-endian switch. inline void Reverse32(void *ptr) { ReverseN(ptr, 4); } // Reverse the order of bytes in a 64 bit quantity for big/little-endian switch. inline void Reverse64(void* ptr) { ReverseN(ptr, 8); } #endif // TESSERACT_CCUTIL_HELPERS_H_ tesseract-3.04.01/ccutil/host.h000066400000000000000000000141451266071204500162750ustar00rootroot00000000000000/****************************************************************************** ** Filename: Host.h ** Purpose: This is the system independent typedefs and defines ** Author: MN, JG, MD ** Version: 5.4.1 ** History: 11/7/94 MCD received the modification that Lennart made ** to port to 32 bit world and modify this file so that it ** will be shared between platform. ** 11/9/94 MCD Make MSW32 subset of MSW. Now MSW means ** MicroSoft Window and MSW32 means the 32 bit worlds ** of MicroSoft Window. Therefore you want the environment ** to be MicroSoft Window and in the 32 bit world - ** _WIN32 must be defined by your compiler. ** 11/30/94 MCD Incorporated comments received for more ** readability and the missing typedef for FLOAT. ** 12/1/94 MCD Added PFVOID typedef ** 5/1/95 MCD. Made many changes based on the inputs. ** Changes: ** 1) Rearrange the #ifdef so that there're definitions for ** particular platforms. ** 2) Took out the #define for computer and environment ** that developer can uncomment ** 3) Added __OLDCODE__ where the defines will be ** obsoleted in the next version and advise not to use. ** 4) Added the definitions for the following: ** FILE_HANDLE, MEMORY_HANDLE, BOOL8, ** MAX_INT8, MAX_INT16, MAX_INT32, MAX_UINT8 ** MAX_UINT16, MAX_UINT32, MAX_FLOAT32 ** 06/19/96 MCD. Took out MAX_FLOAT32 ** 07/15/96 MCD. Fixed the comments error ** Add back BOOL8. ** ** (c) Copyright Hewlett-Packard Company, 1988-1996. ** 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 __HOST__ #define __HOST__ /****************************************************************************** ** IMPORTANT!!! ** ** ** ** Defines either _WIN32, __MAC__, __UNIX__, __OS2__, __PM__ to ** use the specified definitions indicated below in the preprocessor settings. ** ** ** ** Also define either __FarProc__ or __FarData__ and __MOTO__ to use the ** specified definitions indicated below in the preprocessor settings. ** ** ** ** If a preprocessor settings is not allow in the compiler that is being use, ** then it is recommended that a "platform.h" is created with the definition ** of the computer and/or operating system. ******************************************************************************/ #include "platform.h" /* _WIN32 */ #ifdef _WIN32 #include #include // winbase.h contains windows.h #endif /********************************************************/ /* __MAC__ */ #ifdef __MAC__ #include /*----------------------------*/ /*----------------------------*/ #endif /********************************************************/ #if defined(__UNIX__) || defined( __DOS__ ) || defined(__OS2__) || defined(__PM__) /*----------------------------*/ /* FarProc and FarData */ /*----------------------------*/ /*----------------------------*/ #endif /***************************************************************************** ** ** Standard GHC Definitions ** *****************************************************************************/ #ifdef __MOTO__ #define __NATIVE__ MOTO #else #define __NATIVE__ INTEL #endif //typedef HANDLE FD* PHANDLE; // definitions of portable data types (numbers and characters) typedef SIGNED char inT8; typedef unsigned char uinT8; typedef short inT16; typedef unsigned short uinT16; typedef int inT32; typedef unsigned int uinT32; #if (_MSC_VER >= 1200) //%%% vkr for VC 6.0 typedef INT64 inT64; typedef UINT64 uinT64; #else typedef long long int inT64; typedef unsigned long long int uinT64; #endif //%%% vkr for VC 6.0 typedef float FLOAT32; typedef double FLOAT64; typedef unsigned char BOOL8; #define INT32FORMAT "%d" #define INT64FORMAT "%lld" #define MAX_INT8 0x7f #define MAX_INT16 0x7fff #define MAX_INT32 0x7fffffff #define MAX_UINT8 0xff #define MAX_UINT16 0xffff #define MAX_UINT32 0xffffffff #define MAX_FLOAT32 ((float)3.40282347e+38) #define MIN_INT8 0x80 #define MIN_INT16 0x8000 #define MIN_INT32 static_cast(0x80000000) #define MIN_UINT8 0x00 #define MIN_UINT16 0x0000 #define MIN_UINT32 0x00000000 #define MIN_FLOAT32 ((float)1.17549435e-38) // Defines #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif // Return true if x is within tolerance of y template bool NearlyEqual(T x, T y, T tolerance) { T diff = x - y; return diff <= tolerance && -diff <= tolerance; } #endif tesseract-3.04.01/ccutil/indexmapbidi.cpp000066400000000000000000000216101266071204500203030ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: indexmapbidi.cpp // Description: Bi-directional mapping between a sparse and compact space. // Author: rays@google.com (Ray Smith) // Created: Tue Apr 06 11:33:59 PDT 2010 // // (C) Copyright 2010, 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 "indexmapbidi.h" namespace tesseract { // SparseToCompact takes a sparse index to an index in the compact space. // Uses a binary search to find the result. For faster speed use // IndexMapBiDi, but that takes more memory. int IndexMap::SparseToCompact(int sparse_index) const { int result = compact_map_.binary_search(sparse_index); return compact_map_[result] == sparse_index ? result : -1; } // Copy from the input. void IndexMap::CopyFrom(const IndexMap& src) { sparse_size_ = src.sparse_size_; compact_map_ = src.compact_map_; } void IndexMap::CopyFrom(const IndexMapBiDi& src) { sparse_size_ = src.SparseSize(); compact_map_ = src.compact_map_; } // Writes to the given file. Returns false in case of error. bool IndexMap::Serialize(FILE* fp) const { inT32 sparse_size = sparse_size_; if (fwrite(&sparse_size, sizeof(sparse_size), 1, fp) != 1) return false; if (!compact_map_.Serialize(fp)) return false; return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool IndexMap::DeSerialize(bool swap, FILE* fp) { inT32 sparse_size; if (fread(&sparse_size, sizeof(sparse_size), 1, fp) != 1) return false; if (swap) ReverseN(&sparse_size, sizeof(sparse_size)); sparse_size_ = sparse_size; if (!compact_map_.DeSerialize(swap, fp)) return false; return true; } // Top-level init function in a single call to initialize a map to select // a single contiguous subrange [start, end) of the sparse space to be mapped // 1 to 1 to the compact space, with all other elements of the sparse space // left unmapped. // No need to call Setup after this. void IndexMapBiDi::InitAndSetupRange(int sparse_size, int start, int end) { Init(sparse_size, false); for (int i = start; i < end; ++i) SetMap(i, true); Setup(); } // Initializes just the sparse_map_ to the given size with either all // forward indices mapped (all_mapped = true) or none (all_mapped = false). // Call Setup immediately after, or make calls to SetMap first to adjust the // mapping and then call Setup before using the map. void IndexMapBiDi::Init(int size, bool all_mapped) { sparse_map_.init_to_size(size, -1); if (all_mapped) { for (int i = 0; i < size; ++i) sparse_map_[i] = i; } } // Sets a given index in the sparse_map_ to be mapped or not. void IndexMapBiDi::SetMap(int sparse_index, bool mapped) { sparse_map_[sparse_index] = mapped ? 0 : -1; } // Sets up the sparse_map_ and compact_map_ properly after Init and // some calls to SetMap. Assumes an ordered 1-1 map from set indices // in the forward map to the compact space. void IndexMapBiDi::Setup() { int compact_size = 0; for (int i = 0; i < sparse_map_.size(); ++i) { if (sparse_map_[i] >= 0) { sparse_map_[i] = compact_size++; } } compact_map_.init_to_size(compact_size, -1); for (int i = 0; i < sparse_map_.size(); ++i) { if (sparse_map_[i] >= 0) { compact_map_[sparse_map_[i]] = i; } } sparse_size_ = sparse_map_.size(); } // Copy from the input. void IndexMapBiDi::CopyFrom(const IndexMapBiDi& src) { sparse_map_ = src.sparse_map_; compact_map_ = src.compact_map_; sparse_size_ = sparse_map_.size(); } // Merges the two compact space indices. May be called many times, but // the merges must be concluded by a call to CompleteMerges. // Returns true if a merge was actually performed. bool IndexMapBiDi::Merge(int compact_index1, int compact_index2) { // Find the current master index for index1 and index2. compact_index1 = MasterCompactIndex(compact_index1); compact_index2 = MasterCompactIndex(compact_index2); // Be sure that index1 < index2. if (compact_index1 > compact_index2) { int tmp = compact_index1; compact_index1 = compact_index2; compact_index2 = tmp; } else if (compact_index1 == compact_index2) { return false; } // To save iterating over all sparse_map_ entries, simply make the master // entry for index2 point to index1. // This leaves behind a potential chain of parents that needs to be chased, // as above. sparse_map_[compact_map_[compact_index2]] = compact_index1; if (compact_index1 >= 0) compact_map_[compact_index2] = compact_map_[compact_index1]; return true; } // Completes one or more Merge operations by further compacting the // compact space. Unused compact space indices are removed, and the used // ones above shuffled down to fill the gaps. // Example: // Input sparse_map_: (x indicates -1) // x x 0 x 2 x x 4 x 0 x 2 x // Output sparse_map_: // x x 0 x 1 x x 2 x 0 x 1 x // Output compact_map_: // 2 4 7. void IndexMapBiDi::CompleteMerges() { // Ensure each sparse_map_entry contains a master compact_map_ index. int compact_size = 0; for (int i = 0; i < sparse_map_.size(); ++i) { int compact_index = MasterCompactIndex(sparse_map_[i]); sparse_map_[i] = compact_index; if (compact_index >= compact_size) compact_size = compact_index + 1; } // Re-generate the compact_map leaving holes for unused indices. compact_map_.init_to_size(compact_size, -1); for (int i = 0; i < sparse_map_.size(); ++i) { if (sparse_map_[i] >= 0) { if (compact_map_[sparse_map_[i]] == -1) compact_map_[sparse_map_[i]] = i; } } // Compact the compact_map, leaving tmp_compact_map saying where each // index went to in the compacted map. GenericVector tmp_compact_map; tmp_compact_map.init_to_size(compact_size, -1); compact_size = 0; for (int i = 0; i < compact_map_.size(); ++i) { if (compact_map_[i] >= 0) { tmp_compact_map[i] = compact_size; compact_map_[compact_size++] = compact_map_[i]; } } compact_map_.truncate(compact_size); // Now modify the entries in the sparse map to point to the new locations. for (int i = 0; i < sparse_map_.size(); ++i) { if (sparse_map_[i] >= 0) { sparse_map_[i] = tmp_compact_map[sparse_map_[i]]; } } } // Writes to the given file. Returns false in case of error. bool IndexMapBiDi::Serialize(FILE* fp) const { if (!IndexMap::Serialize(fp)) return false; // Make a vector containing the rest of the map. If the map is many-to-one // then each additional sparse entry needs to be stored. // Normally we store only the compact map to save space. GenericVector remaining_pairs; for (int i = 0; i < sparse_map_.size(); ++i) { if (sparse_map_[i] >= 0 && compact_map_[sparse_map_[i]] != i) { remaining_pairs.push_back(i); remaining_pairs.push_back(sparse_map_[i]); } } if (!remaining_pairs.Serialize(fp)) return false; return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool IndexMapBiDi::DeSerialize(bool swap, FILE* fp) { if (!IndexMap::DeSerialize(swap, fp)) return false; GenericVector remaining_pairs; if (!remaining_pairs.DeSerialize(swap, fp)) return false; sparse_map_.init_to_size(sparse_size_, -1); for (int i = 0; i < compact_map_.size(); ++i) { sparse_map_[compact_map_[i]] = i; } for (int i = 0; i < remaining_pairs.size(); ++i) { int sparse_index = remaining_pairs[i++]; sparse_map_[sparse_index] = remaining_pairs[i]; } return true; } // Bulk calls to SparseToCompact. // Maps the given array of sparse indices to an array of compact indices. // Assumes the input is sorted. The output indices are sorted and uniqued. // Return value is the number of "missed" features, being features that // don't map to the compact feature space. int IndexMapBiDi::MapFeatures(const GenericVector& sparse, GenericVector* compact) const { compact->truncate(0); int num_features = sparse.size(); int missed_features = 0; int prev_good_feature = -1; for (int f = 0; f < num_features; ++f) { int feature = sparse_map_[sparse[f]]; if (feature >= 0) { if (feature != prev_good_feature) { compact->push_back(feature); prev_good_feature = feature; } } else { ++missed_features; } } return missed_features; } } // namespace tesseract. tesseract-3.04.01/ccutil/indexmapbidi.h000066400000000000000000000163161266071204500177570ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: indexmapbidi.h // Description: Bi-directional mapping between a sparse and compact space. // Author: rays@google.com (Ray Smith) // Created: Tue Apr 06 11:33:59 PDT 2010 // // (C) Copyright 2010, 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_INDEXMAPBIDI_H_ #define TESSERACT_CCUTIL_INDEXMAPBIDI_H_ #include #include "genericvector.h" namespace tesseract { class IndexMapBiDi; // Bidirectional one-to-one mapping between a sparse and a compact discrete // space. Many entries in the sparse space are unmapped, but those that are // mapped have a 1-1 mapping to (and from) the compact space, where all // values are used. This is useful for forming subsets of larger collections, // such as subsets of character sets, or subsets of binary feature spaces. // // This base class provides basic functionality with binary search for the // SparseToCompact mapping to save memory. // For a faster inverse mapping, or to allow a many-to-one mapping, use // IndexMapBiDi below. // NOTE: there are currently no methods to setup an IndexMap on its own! // It must be initialized by copying from an IndexMapBiDi or by DeSerialize. class IndexMap { public: virtual ~IndexMap() {} // SparseToCompact takes a sparse index to an index in the compact space. // Uses a binary search to find the result. For faster speed use // IndexMapBiDi, but that takes more memory. virtual int SparseToCompact(int sparse_index) const; // CompactToSparse takes a compact index to the corresponding index in the // sparse space. int CompactToSparse(int compact_index) const { return compact_map_[compact_index]; } // The size of the sparse space. virtual int SparseSize() const { return sparse_size_; } // The size of the compact space. int CompactSize() const { return compact_map_.size(); } // Copy from the input. void CopyFrom(const IndexMap& src); void CopyFrom(const IndexMapBiDi& src); // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); protected: // The sparse space covers integers in the range [0, sparse_size_-1]. int sparse_size_; // The compact space covers integers in the range [0, compact_map_.size()-1]. // Each element contains the corresponding sparse index. GenericVector compact_map_; }; // Bidirectional many-to-one mapping between a sparse and a compact discrete // space. As with IndexMap, many entries may be unmapped, but unlike IndexMap, // of those that are, many may be mapped to the same compact index. // If the map is many-to-one, it is not possible to directly obtain all the // sparse indices that map to a single compact index. // This map is time- rather than space-efficient. It stores the entire sparse // space. // IndexMapBiDi may be initialized in one of 3 ways: // 1. Init(size, true); // Setup(); // Sets a complete 1:1 mapping with no unmapped elements. // 2. Init(size, false); // for ... SetMap(index, true); // Setup(); // Specifies precisely which sparse indices are mapped. The mapping is 1:1. // 3. Either of the above, followed by: // for ... Merge(index1, index2); // CompleteMerges(); // Allows a many-to-one mapping by merging compact space indices. class IndexMapBiDi : public IndexMap { public: virtual ~IndexMapBiDi() {} // Top-level init function in a single call to initialize a map to select // a single contiguous subrange [start, end) of the sparse space to be mapped // 1 to 1 to the compact space, with all other elements of the sparse space // left unmapped. // No need to call Setup after this. void InitAndSetupRange(int sparse_size, int start, int end); // Initializes just the sparse_map_ to the given size with either all // forward indices mapped (all_mapped = true) or none (all_mapped = false). // Call Setup immediately after, or make calls to SetMap first to adjust the // mapping and then call Setup before using the map. void Init(int size, bool all_mapped); // Sets a given index in the sparse_map_ to be mapped or not. void SetMap(int sparse_index, bool mapped); // Sets up the sparse_map_ and compact_map_ properly after Init and // some calls to SetMap. Assumes an ordered 1-1 map from set indices // in the sparse space to the compact space. void Setup(); // Merges the two compact space indices. May be called many times, but // the merges must be concluded by a call to CompleteMerges. // Returns true if a merge was actually performed. bool Merge(int compact_index1, int compact_index2); // Returns true if the given compact index has been deleted. bool IsCompactDeleted(int index) const { return MasterCompactIndex(index) < 0; } // Completes one or more Merge operations by further compacting the // compact space. void CompleteMerges(); // SparseToCompact takes a sparse index to an index in the compact space. virtual int SparseToCompact(int sparse_index) const { return sparse_map_[sparse_index]; } // The size of the sparse space. virtual int SparseSize() const { return sparse_map_.size(); } // Copy from the input. void CopyFrom(const IndexMapBiDi& src); // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); // Bulk calls to SparseToCompact. // Maps the given array of sparse indices to an array of compact indices. // Assumes the input is sorted. The output indices are sorted and uniqued. // Return value is the number of "missed" features, being features that // don't map to the compact feature space. int MapFeatures(const GenericVector& sparse, GenericVector* compact) const; private: // Returns the master compact index for a given compact index. // During a multiple merge operation, several compact indices may be // combined, so we need to be able to find the master of all. int MasterCompactIndex(int compact_index) const { while (compact_index >= 0 && sparse_map_[compact_map_[compact_index]] != compact_index) compact_index = sparse_map_[compact_map_[compact_index]]; return compact_index; } // Direct look-up of the compact index for each element in sparse space. GenericVector sparse_map_; }; } // namespace tesseract. #endif // TESSERACT_CCUTIL_INDEXMAPBIDI_H_ tesseract-3.04.01/ccutil/kdpair.h000066400000000000000000000154761266071204500166020ustar00rootroot00000000000000// 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_ tesseract-3.04.01/ccutil/lsterr.h000066400000000000000000000034341266071204500166320ustar00rootroot00000000000000/********************************************************************** * File: lsterr.h (Formerly listerr.h) * Description: Errors shared by list modules * Author: Phil Cheatle * Created: Wed Jan 23 09:10:35 GMT 1991 * * (C) Copyright 1990, 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. * **********************************************************************/ #include "errcode.h" //must be last include #ifndef LSTERR_H #define LSTERR_H const ERRCODE DONT_CONSTRUCT_LIST_BY_COPY = "Can't create a list by assignment"; const ERRCODE DONT_ASSIGN_LISTS = "Can't assign to lists"; const ERRCODE SERIALISE_LINKS = "Attempted to (de)serialise a link element"; #ifndef NDEBUG const ERRCODE NO_LIST = "Iterator not set to a list"; const ERRCODE NULL_OBJECT = "List found this = NULL!"; const ERRCODE NULL_DATA = "List would have returned a NULL data pointer"; const ERRCODE NULL_CURRENT = "List current position is NULL"; const ERRCODE NULL_NEXT = "Next element on the list is NULL"; const ERRCODE NULL_PREV = "Previous element on the list is NULL"; const ERRCODE EMPTY_LIST = "List is empty"; const ERRCODE BAD_PARAMETER = "List parameter error"; const ERRCODE STILL_LINKED = "Attempting to add an element with non NULL links, to a list"; #endif #endif tesseract-3.04.01/ccutil/mainblk.cpp000066400000000000000000000072011266071204500172630ustar00rootroot00000000000000/********************************************************************** * File: mainblk.c (Formerly main.c) * Description: Function to call from main() to setup. * Author: Ray Smith * Created: Tue Oct 22 11:09:40 BST 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include "fileerr.h" #ifdef __UNIX__ #include #include #else #include #endif #include #include "ccutil.h" #define VARDIR "configs/" /**< variables files */ #define EXTERN const ERRCODE NO_PATH = "Warning:explicit path for executable will not be used for configs"; static const ERRCODE USAGE = "Usage"; namespace tesseract { /********************************************************************** * main_setup * * Main for mithras demo program. Read the arguments and set up globals. **********************************************************************/ /** * @brief CCUtil::main_setup - set location of tessdata and name of image * * @param argv0 - paths to the directory with language files and config files. * An actual value of argv0 is used if not NULL, otherwise TESSDATA_PREFIX is * used if not NULL, next try to use compiled in -DTESSDATA_PREFIX. If previous * is not successful - use current directory. * @param basename - name of image */ void CCUtil::main_setup(const char *argv0, const char *basename) { imagebasename = basename; /**< name of image */ char *tessdata_prefix = getenv("TESSDATA_PREFIX"); if (argv0 != NULL) { /* Use tessdata prefix from the command line. */ datadir = argv0; } else if (tessdata_prefix) { /* Use tessdata prefix from the environment. */ datadir = tessdata_prefix; #if defined(_WIN32) } else if (datadir == NULL || access(datadir.string(), 0) != 0) { /* Look for tessdata in directory of executable. */ static char dir[128]; static char exe[128]; DWORD length = GetModuleFileName(NULL, exe, sizeof(exe)); if (length > 0 && length < sizeof(exe)) { _splitpath(exe, NULL, dir, NULL, NULL); datadir = dir; } #endif /* _WIN32 */ #if defined(TESSDATA_PREFIX) } else { /* Use tessdata prefix which was compiled in. */ #define _STR(a) #a #define _XSTR(a) _STR(a) datadir = _XSTR(TESSDATA_PREFIX); #undef _XSTR #undef _STR #endif } // datadir may still be empty: if (datadir.length() == 0) { datadir = "./"; } else { // Remove tessdata from the end if present, as we will add it back! int length = datadir.length(); if (length >= 8 && strcmp(&datadir[length - 8], "tessdata") == 0) datadir.truncate_at(length - 8); else if (length >= 9 && strcmp(&datadir[length - 9], "tessdata/") == 0) datadir.truncate_at(length - 9); } // check for missing directory separator const char *lastchar = datadir.string(); lastchar += datadir.length() - 1; if ((strcmp(lastchar, "/") != 0) && (strcmp(lastchar, "\\") != 0)) datadir += "/"; datadir += m_data_sub_dir; /**< data directory */ } } // namespace tesseract tesseract-3.04.01/ccutil/memry.cpp000066400000000000000000000035311266071204500170010ustar00rootroot00000000000000/********************************************************************** * File: memry.c (Formerly memory.c) * Description: Memory allocation with builtin safety checks. * Author: Ray Smith * Created: Wed Jan 22 09:43:33 GMT 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. * **********************************************************************/ #include "memry.h" #include // With improvements in OS memory allocators, internal memory management // is no longer required, so all these functions now map to their malloc // family equivalents. // TODO(rays) further cleanup by redirecting calls to new and creating proper // constructors. char *alloc_string(inT32 count) { // Round up the amount allocated to a multiple of 4 return static_cast(malloc((count + 3) & ~3)); } void free_string(char *string) { free(string); } void* alloc_struct(inT32 count, const char *) { return malloc(count); } void free_struct(void *deadstruct, inT32, const char *) { free(deadstruct); } void *alloc_mem(inT32 count) { return malloc(static_cast(count)); } void *alloc_big_zeros(inT32 count) { return calloc(static_cast(count), 1); } void free_mem(void *oldchunk) { free(oldchunk); } void free_big_mem(void *oldchunk) { free(oldchunk); } tesseract-3.04.01/ccutil/memry.h000066400000000000000000000031321266071204500164430ustar00rootroot00000000000000/********************************************************************** * File: memry.h (Formerly memory.h) * Description: Header file for basic memory allocation/deallocation. * Author: Ray Smith * Created: Tue May 8 16:03:48 BST 1990 * * (C) Copyright 1990, 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 MEMRY_H #define MEMRY_H #include #include "host.h" // allocate string extern char *alloc_string(inT32 count); // free a string. extern void free_string(char *string); // allocate memory extern void *alloc_struct(inT32 count, const char *name = NULL); // free a structure. extern void free_struct(void *deadstruct, inT32, const char *name = NULL); // get some memory extern void *alloc_mem(inT32 count); // get some memory initialized to 0. extern void *alloc_big_zeros(inT32 count); // free mem from alloc_mem extern void free_mem(void *oldchunk); // free mem from alloc_big_zeros extern void free_big_mem(void *oldchunk); #endif tesseract-3.04.01/ccutil/ndminx.h000066400000000000000000000021111266071204500166030ustar00rootroot00000000000000/********************************************************************** * File: ndminx.h (Formerly ndminmax.h) * Description: Extended ascii chars * Author: Phil Cheatle * Created: Mon Mar 29 14:46:01 BST 1993 * * (C) Copyright 1991, 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 NDMINX_H #define NDMINX_H #ifndef MAX #define MAX(x,y) (((x) >= (y))?(x):(y)) #endif #ifndef MIN #define MIN(x,y) (((x) <= (y))?(x):(y)) #endif #endif tesseract-3.04.01/ccutil/nwmain.h000066400000000000000000000123621266071204500166100ustar00rootroot00000000000000/********************************************************************** * File: nwmain.h * Description: Tool to declare main, making windows invisible. * Author: Ray Smith * Created: Fri Sep 07 13:27:50 MDT 1995 * * (C) Copyright 1995, Hewlett-Packard Co. ** 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 RUNMAIN_H #define RUNMAIN_H #include "host.h" #include "params.h" #define DECLARE_MAIN(ARGC,ARGV)\ STRING_VAR(init_config_file,"config","Config file to read on startup");\ REALLY_DECLARE_MAIN(ARGC,ARGV) #define DECLARE_MAIN_CONFIG(ARGC,ARGV,NAME)\ STRING_VAR(init_config_file,NAME,"Config file to read on startup");\ REALLY_DECLARE_MAIN(ARGC,ARGV) #ifndef __UNIX__ #define REALLY_DECLARE_MAIN(ARGC,ARGV)\ \ /**********************************************************************\ * parse_args\ *\ * Turn a list of args into a new list of args with each separate\ * whitespace spaced string being an arg.\ **********************************************************************/\ \ inT32 parse_args( /*refine arg list*/\ inT32 argc, /*no of input args*/\ char *argv[], /*input args*/\ char *arglist[] /*output args*/\ )\ {\ inT32 argcount; /*converted argc*/\ char *testchar; /*char in option string*/\ inT32 arg; /*current argument*/\ \ argcount=0; /*no of options*/\ for (arg=0;argm_pszExeName);\ argsin[1]=strdup(theapp->m_lpCmdLine);\ /*allocate memory for the args. There can never be more than half*/\ /*the total number of characters in the arguments.*/\ argv=(char**)malloc(((strlen(argsin[0])+strlen(argsin[1]))/2+1)*sizeof(char*));\ \ /*now construct argv as it should be for C.*/\ argc=parse_args(2,argsin,argv);\ \ /*call main(argc,argv) here*/\ exit_code=real_main(argc,(const char **)argv);\ \ \ /*now get rid of the main app window*/\ if (theapp!=NULL && theapp->m_pMainWnd!=NULL)\ PostMessage(theapp->m_pMainWnd->m_hWnd,WM_QUIT,0,0);\ free(argsin[0]);\ free(argsin[1]);\ free(argv);\ global_exit_code=exit_code;\ return exit_code;\ }\ \ inT32 real_main(inT32 ARGC,const char* ARGV[])\ #else #define REALLY_DECLARE_MAIN(ARGC,ARGV)\ \ /**********************************************************************\ * parse_args\ *\ * Turn a list of args into a new list of args with each separate\ * whitespace spaced string being an arg.\ **********************************************************************/\ \ inT32 parse_args( /*refine arg list*/\ inT32 argc, /*no of input args*/\ char *argv[], /*input args*/\ char *arglist[] /*output args*/\ )\ {\ inT32 argcount; /*converted argc*/\ char *testchar; /*char in option string*/\ inT32 arg; /*current argument*/\ \ argcount=0; /*no of options*/\ for (arg=0;arg 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_ tesseract-3.04.01/ccutil/ocrclass.h000066400000000000000000000160551266071204500171330ustar00rootroot00000000000000/********************************************************************** * File: ocrclass.h * Description: Class definitions and constants for the OCR API. * Author: Hewlett-Packard Co * * (C) Copyright 1996, Hewlett-Packard Co. ** 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 typedefs for all the structures used by * the HP OCR interface. * The code is designed to be used with either a C or C++ compiler. * The structures are designed to allow them to be used with any * structure alignment up to 8. **********************************************************************/ #ifndef CCUTIL_OCRCLASS_H_ #define CCUTIL_OCRCLASS_H_ #ifndef __GNUC__ #ifdef _WIN32 #include #include "gettimeofday.h" #endif #else #include #endif #include #include "host.h" /*Maximum lengths of various strings*/ #define MAX_FONT_NAME 34 /*name of font */ #define MAX_OCR_NAME 32 /*name of engine */ #define MAX_OCR_VERSION 17 /*version code of engine */ /*pitch set definitions are identical to RTF*/ #define PITCH_DEF 0 /*default */ #define PITCH_FIXED 1 /*fixed pitch */ #define PITCH_VAR 2 /*variable pitch */ /********************************************************************** * EANYCODE_CHAR * Description of a single character. The character code is defined by * the character set of the current font. * Output text is sent as an array of these structures. * Spaces and line endings in the output are represented in the * structures of the surrounding characters. They are not directly * represented as characters. * The first character in a word has a positive value of blanks. * Missing information should be set to the defaults in the comments. * If word bounds are known, but not character bounds, then the top and * bottom of each character should be those of the word. The left of the * first and right of the last char in each word should be set. All other * lefts and rights should be set to -1. * If set, the values of right and bottom are left+width and top+height. * Most of the members come directly from the parameters to ocr_append_char. * The formatting member uses the enhancement parameter and combines the * line direction stuff into the top 3 bits. * The coding is 0=RL char, 1=LR char, 2=DR NL, 3=UL NL, 4=DR Para, * 5=UL Para, 6=TB char, 7=BT char. API users do not need to know what * the coding is, only that it is backwards compatible with the previous * version. **********************************************************************/ typedef struct { /*single character */ // It should be noted that the format for char_code for version 2.0 and beyond // is UTF8 which means that ASCII characters will come out as one structure but // other characters will be returned in two or more instances of this structure // with a single byte of the UTF8 code in each, but each will have the same // bounding box. Programs which want to handle languagues with different // characters sets will need to handle extended characters appropriately, but // *all* code needs to be prepared to receive UTF8 coded characters for // characters such as bullet and fancy quotes. uinT16 char_code; /*character itself */ inT16 left; /*of char (-1) */ inT16 right; /*of char (-1) */ inT16 top; /*of char (-1) */ inT16 bottom; /*of char (-1) */ inT16 font_index; /*what font (0) */ uinT8 confidence; /*0=perfect, 100=reject (0/100) */ uinT8 point_size; /*of char, 72=i inch, (10) */ inT8 blanks; /*no of spaces before this char (1) */ uinT8 formatting; /*char formatting (0) */ } EANYCODE_CHAR; /*single character */ /********************************************************************** * ETEXT_DESC * Description of the output of the OCR engine. * This structure is used as both a progress monitor and the final * output header, since it needs to be a valid progress monitor while * the OCR engine is storing its output to shared memory. * During progress, all the buffer info is -1. * Progress starts at 0 and increases to 100 during OCR. No other constraint. * Every progress callback, the OCR engine must set ocr_alive to 1. * The HP side will set ocr_alive to 0. Repeated failure to reset * to 1 indicates that the OCR engine is dead. * If the cancel function is not null then it is called with the number of * user words found. If it returns true then operation is cancelled. **********************************************************************/ typedef bool (*CANCEL_FUNC)(void* cancel_this, int words); class ETEXT_DESC { // output header public: inT16 count; // chars in this buffer(0) inT16 progress; // percent complete increasing (0-100) inT8 more_to_come; // true if not last volatile inT8 ocr_alive; // ocr sets to 1, HP 0 inT8 err_code; // for errcode use CANCEL_FUNC cancel; // returns true to cancel void* cancel_this; // this or other data for cancel struct timeval end_time; // time to stop. expected to be set only by call // to set_deadline_msecs() EANYCODE_CHAR text[1]; // character data ETEXT_DESC() : count(0), progress(0), more_to_come(0), ocr_alive(0), err_code(0), cancel(NULL), cancel_this(NULL) { end_time.tv_sec = 0; end_time.tv_usec = 0; } // Sets the end time to be deadline_msecs milliseconds from now. void set_deadline_msecs(inT32 deadline_msecs) { gettimeofday(&end_time, NULL); inT32 deadline_secs = deadline_msecs / 1000; end_time.tv_sec += deadline_secs; end_time.tv_usec += (deadline_msecs - deadline_secs * 1000) * 1000; if (end_time.tv_usec > 1000000) { end_time.tv_usec -= 1000000; ++end_time.tv_sec; } } // Returns false if we've not passed the end_time, or have not set a deadline. bool deadline_exceeded() const { if (end_time.tv_sec == 0 && end_time.tv_usec == 0) return false; struct timeval now; gettimeofday(&now, NULL); return (now.tv_sec > end_time.tv_sec || (now.tv_sec == end_time.tv_sec && now.tv_usec > end_time.tv_usec)); } }; #endif // CCUTIL_OCRCLASS_H_ tesseract-3.04.01/ccutil/params.cpp000066400000000000000000000202101266071204500171240ustar00rootroot00000000000000/********************************************************************** * File: params.cpp * Description: Initialization and setting of Tesseract parameters. * Author: Ray Smith * Created: Fri Feb 22 16:22:34 GMT 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include #include #include #include "genericvector.h" #include "scanutils.h" #include "tprintf.h" #include "params.h" #define PLUS '+' //flag states #define MINUS '-' #define EQUAL '=' tesseract::ParamsVectors *GlobalParams() { static tesseract::ParamsVectors *global_params = new tesseract::ParamsVectors(); return global_params; } namespace tesseract { bool ParamUtils::ReadParamsFile(const char *file, SetParamConstraint constraint, ParamsVectors *member_params) { inT16 nameoffset; // offset for real name FILE *fp; // file pointer // iterators if (*file == PLUS) { nameoffset = 1; } else if (*file == MINUS) { nameoffset = 1; } else { nameoffset = 0; } fp = fopen(file + nameoffset, "rb"); if (fp == NULL) { tprintf("read_params_file: Can't open %s\n", file + nameoffset); return true; } const bool anyerr = ReadParamsFromFp(fp, -1, constraint, member_params); fclose(fp); return anyerr; } bool ParamUtils::ReadParamsFromFp(FILE *fp, inT64 end_offset, SetParamConstraint constraint, ParamsVectors *member_params) { char line[MAX_PATH]; // input line bool anyerr = false; // true if any error bool foundit; // found parameter char *valptr; // value field while ((end_offset < 0 || ftell(fp) < end_offset) && fgets(line, MAX_PATH, fp)) { if (line[0] != '\n' && line[0] != '#') { chomp_string(line); // remove newline for (valptr = line; *valptr && *valptr != ' ' && *valptr != '\t'; valptr++); if (*valptr) { // found blank *valptr = '\0'; // make name a string do valptr++; // find end of blanks while (*valptr == ' ' || *valptr == '\t'); } foundit = SetParam(line, valptr, constraint, member_params); if (!foundit) { anyerr = true; // had an error tprintf("read_params_file: parameter not found: %s\n", line); exit(1); } } } return anyerr; } bool ParamUtils::SetParam(const char *name, const char* value, SetParamConstraint constraint, ParamsVectors *member_params) { // Look for the parameter among string parameters. StringParam *sp = FindParam(name, GlobalParams()->string_params, member_params->string_params); if (sp != NULL && sp->constraint_ok(constraint)) sp->set_value(value); if (*value == '\0') return (sp != NULL); // Look for the parameter among int parameters. int intval; IntParam *ip = FindParam(name, GlobalParams()->int_params, member_params->int_params); if (ip && ip->constraint_ok(constraint) && sscanf(value, INT32FORMAT, &intval) == 1) ip->set_value(intval); // Look for the parameter among bool parameters. BoolParam *bp = FindParam(name, GlobalParams()->bool_params, member_params->bool_params); if (bp != NULL && bp->constraint_ok(constraint)) { if (*value == 'T' || *value == 't' || *value == 'Y' || *value == 'y' || *value == '1') { bp->set_value(true); } else if (*value == 'F' || *value == 'f' || *value == 'N' || *value == 'n' || *value == '0') { bp->set_value(false); } } // Look for the parameter among double parameters. double doubleval; DoubleParam *dp = FindParam(name, GlobalParams()->double_params, member_params->double_params); if (dp != NULL && dp->constraint_ok(constraint)) { #ifdef EMBEDDED doubleval = strtofloat(value); #else if (sscanf(value, "%lf", &doubleval) == 1) #endif dp->set_value(doubleval); } return (sp || ip || bp || dp); } bool ParamUtils::GetParamAsString(const char *name, const ParamsVectors* member_params, STRING *value) { // Look for the parameter among string parameters. StringParam *sp = FindParam(name, GlobalParams()->string_params, member_params->string_params); if (sp) { *value = sp->string(); return true; } // Look for the parameter among int parameters. IntParam *ip = FindParam(name, GlobalParams()->int_params, member_params->int_params); if (ip) { char buf[128]; snprintf(buf, sizeof(buf), "%d", inT32(*ip)); *value = buf; return true; } // Look for the parameter among bool parameters. BoolParam *bp = FindParam(name, GlobalParams()->bool_params, member_params->bool_params); if (bp != NULL) { *value = BOOL8(*bp) ? "1": "0"; return true; } // Look for the parameter among double parameters. DoubleParam *dp = FindParam(name, GlobalParams()->double_params, member_params->double_params); if (dp != NULL) { char buf[128]; snprintf(buf, sizeof(buf), "%g", double(*dp)); *value = buf; return true; } return false; } void ParamUtils::PrintParams(FILE *fp, const ParamsVectors *member_params) { int v, i; int num_iterations = (member_params == NULL) ? 1 : 2; for (v = 0; v < num_iterations; ++v) { const ParamsVectors *vec = (v == 0) ? GlobalParams() : member_params; for (i = 0; i < vec->int_params.size(); ++i) { fprintf(fp, "%s\t%d\t%s\n", vec->int_params[i]->name_str(), (inT32)(*vec->int_params[i]), vec->int_params[i]->info_str()); } for (i = 0; i < vec->bool_params.size(); ++i) { fprintf(fp, "%s\t%d\t%s\n", vec->bool_params[i]->name_str(), (BOOL8)(*vec->bool_params[i]), vec->bool_params[i]->info_str()); } for (int i = 0; i < vec->string_params.size(); ++i) { fprintf(fp, "%s\t%s\t%s\n", vec->string_params[i]->name_str(), vec->string_params[i]->string(), vec->string_params[i]->info_str()); } for (int i = 0; i < vec->double_params.size(); ++i) { fprintf(fp, "%s\t%g\t%s\n", vec->double_params[i]->name_str(), (double)(*vec->double_params[i]), vec->double_params[i]->info_str()); } } } // 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 tesseract-3.04.01/ccutil/params.h000066400000000000000000000256711266071204500166110ustar00rootroot00000000000000/********************************************************************** * File: params.h * Description: Class definitions of the *_VAR classes for tunable constants. * Author: Ray Smith * Created: Fri Feb 22 11:26:25 GMT 1991 * * (C) Copyright 1991, 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 PARAMS_H #define PARAMS_H #include #include "genericvector.h" #include "strngs.h" namespace tesseract { class IntParam; class BoolParam; class StringParam; class DoubleParam; // Enum for constraints on what kind of params should be set by SetParam(). enum SetParamConstraint { SET_PARAM_CONSTRAINT_NONE, SET_PARAM_CONSTRAINT_DEBUG_ONLY, SET_PARAM_CONSTRAINT_NON_DEBUG_ONLY, SET_PARAM_CONSTRAINT_NON_INIT_ONLY, }; struct ParamsVectors { GenericVector int_params; GenericVector bool_params; GenericVector string_params; GenericVector double_params; }; // Utility functions for working with Tesseract parameters. class ParamUtils { public: // Reads a file of parameter definitions and set/modify the values therein. // If the filename begins with a + or -, the BoolVariables will be // ORed or ANDed with any current values. // Blank lines and lines beginning # are ignored. // Values may have any whitespace after the name and are the rest of line. static bool ReadParamsFile( const char *file, // filename to read SetParamConstraint constraint, ParamsVectors *member_params); // Read parameters from the given file pointer (stop at end_offset). static bool ReadParamsFromFp(FILE *fp, inT64 end_offset, SetParamConstraint constraint, ParamsVectors *member_params); // Set a parameters to have the given value. static bool SetParam(const char *name, const char* value, SetParamConstraint constraint, ParamsVectors *member_params); // Returns the pointer to the parameter with the given name (of the // appropriate type) if it was found in the vector obtained from // GlobalParams() or in the given member_params. template static T *FindParam(const char *name, const GenericVector &global_vec, const GenericVector &member_vec) { int i; for (i = 0; i < global_vec.size(); ++i) { if (strcmp(global_vec[i]->name_str(), name) == 0) return global_vec[i]; } for (i = 0; i < member_vec.size(); ++i) { if (strcmp(member_vec[i]->name_str(), name) == 0) return member_vec[i]; } return NULL; } // Removes the given pointer to the param from the given vector. template static void RemoveParam(T *param_ptr, GenericVector *vec) { for (int i = 0; i < vec->size(); ++i) { if ((*vec)[i] == param_ptr) { vec->remove(i); return; } } } // Fetches the value of the named param as a STRING. Returns false if not // found. static bool GetParamAsString(const char *name, const ParamsVectors* member_params, STRING *value); // 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. class Param { public: ~Param() {} const char *name_str() const { return name_; } const char *info_str() const { return info_; } bool is_init() const { return init_; } bool is_debug() const { return debug_; } bool constraint_ok(SetParamConstraint constraint) const { return (constraint == SET_PARAM_CONSTRAINT_NONE || (constraint == SET_PARAM_CONSTRAINT_DEBUG_ONLY && this->is_debug()) || (constraint == SET_PARAM_CONSTRAINT_NON_DEBUG_ONLY && !this->is_debug()) || (constraint == SET_PARAM_CONSTRAINT_NON_INIT_ONLY && !this->is_init())); } protected: Param(const char *name, const char *comment, bool init) : name_(name), info_(comment), init_(init) { debug_ = (strstr(name, "debug") != NULL) || (strstr(name, "display")); } const char *name_; // name of this parameter const char *info_; // for menus bool init_; // needs to be set before init bool debug_; }; class IntParam : public Param { public: 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 operator=(inT32 value) { value_ = 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_; }; class BoolParam : public Param { public: 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 operator=(BOOL8 value) { value_ = 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_; }; class StringParam : public Param { public: StringParam(const char *value, const char *name, 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; } bool operator==(const STRING& other) { return value_ == other; } void operator=(const STRING& value) { value_ = value; } 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_; }; class DoubleParam : public Param { public: 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 operator=(double value) { value_ = 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_; }; } // namespace tesseract // Global parameter lists. // // To avoid the problem of undetermined order of static initialization // global_params are accessed through the GlobalParams function that // initializes the static pointer to global_params only on the first // first time GlobalParams() is called. // // TODO(daria): remove GlobalParams() when all global Tesseract // parameters are converted to members. tesseract::ParamsVectors *GlobalParams(); /************************************************************************* * Note on defining parameters. * * The values of the parameters defined with *_INIT_* macros are guaranteed * to be loaded from config files before Tesseract initialization is done * (there is no such guarantee for parameters defined with the other macros). *************************************************************************/ #define INT_VAR_H(name,val,comment)\ tesseract::IntParam name #define BOOL_VAR_H(name,val,comment)\ tesseract::BoolParam name #define STRING_VAR_H(name,val,comment)\ tesseract::StringParam name #define double_VAR_H(name,val,comment)\ tesseract::DoubleParam name #define INT_VAR(name,val,comment)\ tesseract::IntParam name(val,#name,comment,false,GlobalParams()) #define BOOL_VAR(name,val,comment)\ tesseract::BoolParam name(val,#name,comment,false,GlobalParams()) #define STRING_VAR(name,val,comment)\ tesseract::StringParam name(val,#name,comment,false,GlobalParams()) #define double_VAR(name,val,comment)\ tesseract::DoubleParam name(val,#name,comment,false,GlobalParams()) #define INT_INIT_VAR(name,val,comment)\ tesseract::IntParam name(val,#name,comment,true,GlobalParams()) #define BOOL_INIT_VAR(name,val,comment)\ tesseract::BoolParam name(val,#name,comment,true,GlobalParams()) #define STRING_INIT_VAR(name,val,comment)\ tesseract::StringParam name(val,#name,comment,true,GlobalParams()) #define double_INIT_VAR(name,val,comment)\ tesseract::DoubleParam name(val,#name,comment,true,GlobalParams()) #define INT_MEMBER(name, val, comment, vec)\ name(val, #name, comment, false, vec) #define BOOL_MEMBER(name, val, comment, vec)\ name(val, #name, comment, false, vec) #define STRING_MEMBER(name, val, comment, vec)\ name(val, #name, comment, false, vec) #define double_MEMBER(name, val, comment, vec)\ name(val, #name, comment, false, vec) #define INT_INIT_MEMBER(name, val, comment, vec)\ name(val, #name, comment, true, vec) #define BOOL_INIT_MEMBER(name, val, comment, vec)\ name(val, #name, comment, true, vec) #define STRING_INIT_MEMBER(name, val, comment, vec)\ name(val, #name, comment, true, vec) #define double_INIT_MEMBER(name, val, comment, vec)\ name(val, #name, comment, true, vec) #endif tesseract-3.04.01/ccutil/platform.h000066400000000000000000000047301266071204500171430ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: platform.h // Description: Place holder // Author: // Created: // // (C) Copyright 2006, 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_PLATFORM_H__ #define TESSERACT_CCUTIL_PLATFORM_H__ #include #define DLLSYM #ifdef _WIN32 #ifdef __GNUC__ #define ultoa _ultoa #endif /* __GNUC__ */ #define SIGNED #if defined(_MSC_VER) #if (_MSC_VER < 1900) #define snprintf _snprintf #endif #if (_MSC_VER <= 1400) #define vsnprintf _vsnprintf #endif /* (_MSC_VER <= 1400) */ #endif /* defined(_MSC_VER) */ #else #define __UNIX__ #include #ifndef PATH_MAX #define MAX_PATH 4096 #else #define MAX_PATH PATH_MAX #endif #define SIGNED signed #endif #if defined(_WIN32) || defined(__CYGWIN__) #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #endif #if defined(_WIN32) || defined(__CYGWIN__) #if defined(TESS_EXPORTS) #define TESS_API __declspec(dllexport) #elif defined(TESS_IMPORTS) #define TESS_API __declspec(dllimport) #else #define TESS_API #endif #define TESS_LOCAL #else #if __GNUC__ >= 4 #if defined(TESS_EXPORTS) || defined(TESS_IMPORTS) #define TESS_API __attribute__ ((visibility ("default"))) #define TESS_LOCAL __attribute__ ((visibility ("hidden"))) #else #define TESS_API #define TESS_LOCAL #endif #else #define TESS_API #define TESS_LOCAL #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__ tesseract-3.04.01/ccutil/qrsequence.h000066400000000000000000000060171266071204500174720ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: qrsequence.h // Description: Quasi-random sequence generator class. // Author: Ranjith Unnikrishnan // Created: Wed May 20 2009 // // Class to generate a (deterministic) quasi-random Van der Corput sequence that // covers the interval [0,N) without repetition. // // The sequence is generated by reversing the base-2 representation of the // sequence of natural numbers {0, 1,... M-1}, where M is 2^{num_bits_} and // num_bits is the minimum number of bits required to represent N. If a reversed // numbers is >= N it is rejected and the next natural number is considered // until a valid output number is found. // // (C) Copyright 2009, 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_QRSEQUENCE_H_ #define TESSERACT_CCUTIL_QRSEQUENCE_H_ #include class QRSequenceGenerator { public: // Object is initalized with the size of the output range. explicit QRSequenceGenerator(int N) : N_(N), next_num_(0) { num_bits_ = static_cast(ceil(log(static_cast(N)) / log(2.0))); } // Main worker method that retrieves the next number in the sequence. // Returns kInvalidVal if called more than N times after object initialization int GetVal() { const int kInvalidVal = -1; const int kMaxNaturalNumberValue = 1 << num_bits_; if (next_num_ >= kMaxNaturalNumberValue) return kInvalidVal; int n = next_num_; while (next_num_ < kMaxNaturalNumberValue) { n = GetBinaryReversedInteger(next_num_++); if (n < N_) break; } return (next_num_ > kMaxNaturalNumberValue) ? kInvalidVal : n; } protected: // Outputs the integer formed by reversing the bits of the input integer. Only // the lowest num_bits_ bits of the input integer are reversed. int GetBinaryReversedInteger(int in_val) const { int bit_pos = num_bits_; int out_val = 0; while(bit_pos--) { // Set the value of the last bit. out_val |= (in_val & 0x1); if (bit_pos > 0) { // Left-shift output value to prepare for storing the next bit. out_val <<= 1; } // Right-shift input value to prepare for retrieving the next bit. in_val >>= 1; } return out_val; } int N_; // Next number to be considered for reversal and output. int next_num_; // number of bits required to represent the numbers of the sequence int num_bits_; }; #endif // TESSERACT_CCUTIL_QRSEQUENCE_H_ tesseract-3.04.01/ccutil/scanutils.cpp000066400000000000000000000365031266071204500176620ustar00rootroot00000000000000// Copyright 2006 Google Inc. // All Rights Reserved. // Author: renn // // The fscanf, vfscanf and creat functions are implemented so that their // functionality is mostly like their stdio counterparts. However, currently // these functions do not use any buffering, making them rather slow. // File streams are thus processed one character at a time. // Although the implementations of the scanf functions do lack a few minor // features, they should be sufficient for their use in tesseract. // // 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. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include #include #include #include #include #include #include #include #include #include #include "scanutils.h" #include "tprintf.h" // workaround for "'off_t' was not declared in this scope" with -std=c++11 #if !defined(HAVE_OFF_T) typedef long off_t; #endif // off_t enum Flags { FL_SPLAT = 0x01, // Drop the value, do not assign FL_INV = 0x02, // Character-set with inverse FL_WIDTH = 0x04, // Field width specified FL_MINUS = 0x08, // Negative number }; enum Ranks { RANK_CHAR = -2, RANK_SHORT = -1, RANK_INT = 0, RANK_LONG = 1, RANK_LONGLONG = 2, RANK_PTR = INT_MAX // Special value used for pointers }; const enum Ranks kMinRank = RANK_CHAR; const enum Ranks kMaxRank = RANK_LONGLONG; const enum Ranks kIntMaxRank = RANK_LONGLONG; const enum Ranks kSizeTRank = RANK_LONG; const enum Ranks kPtrDiffRank = RANK_LONG; enum Bail { BAIL_NONE = 0, // No error condition BAIL_EOF, // Hit EOF BAIL_ERR // Conversion mismatch }; // Helper functions ------------------------------------------------------------ inline size_t LongBit() { return CHAR_BIT * sizeof(long); } static inline int SkipSpace(FILE *s) { int p; while (isspace(p = fgetc(s))); ungetc(p, s); // Make sure next char is available for reading return p; } static inline void SetBit(unsigned long *bitmap, unsigned int bit) { bitmap[bit/LongBit()] |= 1UL << (bit%LongBit()); } static inline int TestBit(unsigned long *bitmap, unsigned int bit) { return static_cast(bitmap[bit/LongBit()] >> (bit%LongBit())) & 1; } static inline int DigitValue(int ch, int base) { if (ch >= '0' && ch <= '9') { if (base >= 10 || ch <= '7') return ch-'0'; } else if (ch >= 'A' && ch <= 'Z' && base == 16) { return ch-'A'+10; } else if (ch >= 'a' && ch <= 'z' && base == 16) { return ch-'a'+10; } return -1; } // IO (re-)implementations ----------------------------------------------------- uintmax_t streamtoumax(FILE* s, int base) { int minus = 0; uintmax_t v = 0; int d, c = 0; for (c = fgetc(s); isspace(static_cast(c)) && (c != EOF); c = fgetc(s)) {} // Single optional + or - if (c == '-' || c == '+') { minus = (c == '-'); c = fgetc(s); } // Assign correct base if (base == 0) { if (c == '0') { c = fgetc(s); if (c == 'x' || c == 'X') { base = 16; c = fgetc(s); } else { base = 8; } } } else if (base == 16) { if (c == '0') { c = fgetc(s); if (c == 'x' || c == 'X') c = fgetc(s); } } // Actual number parsing for (; (c != EOF) && (d = DigitValue(c, base)) >= 0; c = fgetc(s)) v = v*base + d; ungetc(c, s); return minus ? -v : v; } double streamtofloat(FILE* s) { int minus = 0; int v = 0; int d, c = 0; int k = 1; int w = 0; for (c = fgetc(s); isspace(static_cast(c)) && (c != EOF); c = fgetc(s)); // Single optional + or - if (c == '-' || c == '+') { minus = (c == '-'); c = fgetc(s); } // Actual number parsing for (; c != EOF && (d = DigitValue(c, 10)) >= 0; c = fgetc(s)) v = v*10 + d; if (c == '.') { for (c = fgetc(s); c != EOF && (d = DigitValue(c, 10)) >= 0; c = fgetc(s)) { w = w*10 + d; k *= 10; } } double f = static_cast(v) + static_cast(w) / static_cast(k); if (c == 'e' || c == 'E') { c = fgetc(s); int expsign = 1; if (c == '-' || c == '+') { expsign = (c == '-') ? -1 : 1; c = fgetc(s); } int exponent = 0; for (; (c != EOF) && (d = DigitValue(c, 10)) >= 0; c = fgetc(s)) { exponent = exponent * 10 + d; } exponent *= expsign; f *= pow(10.0, static_cast(exponent)); } ungetc(c, s); return minus ? -f : f; } double strtofloat(const char* s) { int minus = 0; int v = 0; int d; int k = 1; int w = 0; while(*s && isspace(static_cast(*s))) s++; // Single optional + or - if (*s == '-' || *s == '+') { minus = (*s == '-'); s++; } // Actual number parsing for (; *s && (d = DigitValue(*s, 10)) >= 0; s++) v = v*10 + d; if (*s == '.') { for (++s; *s && (d = DigitValue(*s, 10)) >= 0; s++) { w = w*10 + d; k *= 10; } } if (*s == 'e' || *s == 'E') tprintf("WARNING: Scientific Notation not supported!"); double f = static_cast(v) + static_cast(w) / static_cast(k); return minus ? -f : f; } static int tvfscanf(FILE* stream, const char *format, va_list ap); int tfscanf(FILE* stream, const char *format, ...) { va_list ap; int rv; va_start(ap, format); rv = tvfscanf(stream, format, ap); va_end(ap); return rv; } #ifdef EMBEDDED int fscanf(FILE* stream, const char *format, ...) { va_list ap; int rv; va_start(ap, format); rv = tvfscanf(stream, format, ap); va_end(ap); return rv; } int vfscanf(FILE* stream, const char *format, ...) { va_list ap; int rv; va_start(ap, format); rv = tvfscanf(stream, format, ap); va_end(ap); return rv; } #endif static int tvfscanf(FILE* stream, const char *format, va_list ap) { const char *p = format; char ch; int q = 0; uintmax_t val = 0; int rank = RANK_INT; // Default rank unsigned int width = UINT_MAX; int base; int flags = 0; enum { ST_NORMAL, // Ground state ST_FLAGS, // Special flags ST_WIDTH, // Field width ST_MODIFIERS, // Length or conversion modifiers ST_MATCH_INIT, // Initial state of %[ sequence ST_MATCH, // Main state of %[ sequence ST_MATCH_RANGE, // After - in a %[ sequence } state = ST_NORMAL; char *sarg = NULL; // %s %c or %[ string argument enum Bail bail = BAIL_NONE; int sign; int converted = 0; // Successful conversions unsigned long matchmap[((1 << CHAR_BIT)+(CHAR_BIT * sizeof(long) - 1)) / (CHAR_BIT * sizeof(long))]; int matchinv = 0; // Is match map inverted? unsigned char range_start = 0; off_t start_off = ftell(stream); // Skip leading spaces SkipSpace(stream); while ((ch = *p++) && !bail) { switch (state) { case ST_NORMAL: if (ch == '%') { state = ST_FLAGS; flags = 0; rank = RANK_INT; width = UINT_MAX; } else if (isspace(static_cast(ch))) { SkipSpace(stream); } else { if (fgetc(stream) != ch) bail = BAIL_ERR; // Match failure } break; case ST_FLAGS: if (ch == '*') { flags |= FL_SPLAT; } else if ('0' <= ch && ch <= '9') { width = (ch-'0'); state = ST_WIDTH; flags |= FL_WIDTH; } else { state = ST_MODIFIERS; p--; // Process this character again } break; case ST_WIDTH: if (ch >= '0' && ch <= '9') { width = width*10+(ch-'0'); } else { state = ST_MODIFIERS; p--; // Process this character again } break; case ST_MODIFIERS: switch (ch) { // Length modifiers - nonterminal sequences case 'h': rank--; // Shorter rank break; case 'l': rank++; // Longer rank break; case 'j': rank = kIntMaxRank; break; case 'z': rank = kSizeTRank; break; case 't': rank = kPtrDiffRank; break; case 'L': case 'q': rank = RANK_LONGLONG; // long double/long long break; default: // Output modifiers - terminal sequences state = ST_NORMAL; // Next state will be normal if (rank < kMinRank) // Canonicalize rank rank = kMinRank; else if (rank > kMaxRank) rank = kMaxRank; switch (ch) { case 'P': // Upper case pointer case 'p': // Pointer rank = RANK_PTR; base = 0; sign = 0; goto scan_int; case 'i': // Base-independent integer base = 0; sign = 1; goto scan_int; case 'd': // Decimal integer base = 10; sign = 1; goto scan_int; case 'o': // Octal integer base = 8; sign = 0; goto scan_int; case 'u': // Unsigned decimal integer base = 10; sign = 0; goto scan_int; case 'x': // Hexadecimal integer case 'X': base = 16; sign = 0; goto scan_int; case 'n': // Number of characters consumed val = ftell(stream) - start_off; goto set_integer; scan_int: q = SkipSpace(stream); if ( q <= 0 ) { bail = BAIL_EOF; break; } val = streamtoumax(stream, base); // fall through set_integer: if (!(flags & FL_SPLAT)) { converted++; switch(rank) { case RANK_CHAR: *va_arg(ap, unsigned char *) = static_cast(val); break; case RANK_SHORT: *va_arg(ap, unsigned short *) = static_cast(val); break; case RANK_INT: *va_arg(ap, unsigned int *) = static_cast(val); break; case RANK_LONG: *va_arg(ap, unsigned long *) = static_cast(val); break; case RANK_LONGLONG: *va_arg(ap, unsigned long long *) = static_cast(val); break; case RANK_PTR: *va_arg(ap, void **) = reinterpret_cast(static_cast(val)); break; } } break; case 'f': // Preliminary float value parsing case 'g': case 'G': case 'e': case 'E': q = SkipSpace(stream); if (q <= 0) { bail = BAIL_EOF; break; } { double fval = streamtofloat(stream); if (!(flags & FL_SPLAT)) { if (rank == RANK_INT) *va_arg(ap, float *) = static_cast(fval); else if (rank == RANK_LONG) *va_arg(ap, double *) = static_cast(fval); converted++; } } break; case 'c': // Character width = (flags & FL_WIDTH) ? width : 1; // Default width == 1 sarg = va_arg(ap, char *); while (width--) { if ((q = fgetc(stream)) <= 0) { bail = BAIL_EOF; break; } if (!(flags & FL_SPLAT)) { *sarg++ = q; converted++; } } break; case 's': // String { char *sp; sp = sarg = va_arg(ap, char *); while (width--) { q = fgetc(stream); if (isspace(static_cast(q)) || q <= 0) { ungetc(q, stream); break; } if (!(flags & FL_SPLAT)) *sp = q; sp++; } if (sarg == sp) { bail = BAIL_EOF; } else if (!(flags & FL_SPLAT)) { *sp = '\0'; // Terminate output converted++; } else { } } break; case '[': // Character range sarg = va_arg(ap, char *); state = ST_MATCH_INIT; matchinv = 0; memset(matchmap, 0, sizeof matchmap); break; case '%': // %% sequence if (fgetc(stream) != '%' ) bail = BAIL_ERR; break; default: // Anything else bail = BAIL_ERR; // Unknown sequence break; } } break; case ST_MATCH_INIT: // Initial state for %[ match if (ch == '^' && !(flags & FL_INV)) { matchinv = 1; } else { SetBit(matchmap, static_cast(ch)); state = ST_MATCH; } break; case ST_MATCH: // Main state for %[ match if (ch == ']') { goto match_run; } else if (ch == '-') { range_start = static_cast(ch); state = ST_MATCH_RANGE; } else { SetBit(matchmap, static_cast(ch)); } break; case ST_MATCH_RANGE: // %[ match after - if (ch == ']') { SetBit(matchmap, static_cast('-')); goto match_run; } else { int i; for (i = range_start ; i < (static_cast(ch)) ; i++) SetBit(matchmap, i); state = ST_MATCH; } break; match_run: // Match expression finished char* oarg = sarg; while (width) { q = fgetc(stream); unsigned char qc = static_cast(q); if (q <= 0 || !(TestBit(matchmap, qc)^matchinv)) { ungetc(q, stream); break; } if (!(flags & FL_SPLAT)) *sarg = q; sarg++; } if (oarg == sarg) { bail = (q <= 0) ? BAIL_EOF : BAIL_ERR; } else if (!(flags & FL_SPLAT)) { *sarg = '\0'; converted++; } break; } } if (bail == BAIL_EOF && !converted) converted = -1; // Return EOF (-1) return converted; } #ifdef EMBEDDED int creat(const char *pathname, mode_t mode) { return open(pathname, O_CREAT | O_TRUNC | O_WRONLY, mode); } #endif // EMBEDDED tesseract-3.04.01/ccutil/scanutils.h000066400000000000000000000050121266071204500173160ustar00rootroot00000000000000// Copyright 2006 Google Inc. // All Rights Reserved. // Author: renn // // Contains file io functions (mainly for file parsing), that might not be // available, on embedded devices, or that have an incomplete implementation // there. // // 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_SCANUTILS_H_ #define TESSERACT_CCUTIL_SCANUTILS_H_ #include #include #include #include /** * fscanf variant to ensure correct reading regardless of locale. * * tfscanf parse a file stream according to the given format. See the fscanf * manpage for more information, as this function attempts to mimic its * behavior. * * @note Note that scientific floating-point notation is not supported. * */ int tfscanf(FILE* stream, const char *format, ...); #ifdef EMBEDDED // Attempts to parse the given file stream s as an integer of the base // 'base'. Returns the first successfully parsed integer as a uintmax_t, or // 0, if none was found. uintmax_t streamtoumax(FILE* s, int base); // Parse a file stream according to the given format. See the fscanf manpage // for more information, as this function attempts to mimic its behavior. // Note that scientific loating-point notation is not supported. int fscanf(FILE* stream, const char *format, ...); // Parse a file stream according to the given format. See the fscanf manpage // for more information, as this function attempts to mimic its behavior. // Note that scientific loating-point notation is not supported. int vfscanf(FILE* stream, const char *format, va_list ap); // Create a file at the specified path. See the creat manpage for more // information, as this function attempts to mimic its behavior. int creat(const char *pathname, mode_t mode); // Convert the specified C-String to a float. Returns the first parsed float, // or 0.0 if no floating point value could be found. Note that scientific // floating-point notation is not supported. double strtofloat(const char* s); #endif // EMBEDDED #endif // TESSERACT_CCUTIL_SCANUTILS_H_ tesseract-3.04.01/ccutil/serialis.cpp000066400000000000000000000100311266071204500174540ustar00rootroot00000000000000/********************************************************************** * File: serialis.h (Formerly serialmac.h) * Description: Inline routines and macros for serialisation functions * Author: Phil Cheatle * Created: Tue Oct 08 08:33:12 BST 1991 * * (C) Copyright 1990, 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. * **********************************************************************/ #include "serialis.h" #include #include "genericvector.h" namespace tesseract { TFile::TFile() : offset_(0), data_(NULL), data_is_owned_(false), is_writing_(false) { } TFile::~TFile() { if (data_is_owned_) delete data_; } bool TFile::Open(const STRING& filename, FileReader reader) { if (!data_is_owned_) { data_ = new GenericVector; data_is_owned_ = true; } offset_ = 0; is_writing_ = false; if (reader == NULL) return LoadDataFromFile(filename, data_); else return (*reader)(filename, data_); } bool TFile::Open(const char* data, int size) { offset_ = 0; if (!data_is_owned_) { data_ = new GenericVector; data_is_owned_ = true; } is_writing_ = false; data_->init_to_size(size, 0); memcpy(&(*data_)[0], data, size); return true; } bool TFile::Open(FILE* fp, inT64 end_offset) { offset_ = 0; inT64 current_pos = ftell(fp); if (end_offset < 0) { if (fseek(fp, 0, SEEK_END)) return false; end_offset = ftell(fp); if (fseek(fp, current_pos, SEEK_SET)) return false; } int size = end_offset - current_pos; is_writing_ = false; if (!data_is_owned_) { data_ = new GenericVector; data_is_owned_ = true; } data_->init_to_size(size, 0); return static_cast(fread(&(*data_)[0], 1, size, fp)) == size; } char* TFile::FGets(char* buffer, int buffer_size) { ASSERT_HOST(!is_writing_); int size = 0; while (size + 1 < buffer_size && offset_ < data_->size()) { buffer[size++] = (*data_)[offset_++]; if ((*data_)[offset_ - 1] == '\n') break; } if (size < buffer_size) buffer[size] = '\0'; return size > 0 ? buffer : NULL; } int TFile::FRead(void* buffer, int size, int count) { ASSERT_HOST(!is_writing_); int required_size = size * count; if (required_size <= 0) return 0; char* char_buffer = reinterpret_cast(buffer); if (data_->size() - offset_ < required_size) required_size = data_->size() - offset_; if (required_size > 0) memcpy(char_buffer, &(*data_)[offset_], required_size); offset_ += required_size; return required_size / size; } void TFile::Rewind() { ASSERT_HOST(!is_writing_); offset_ = 0; } void TFile::OpenWrite(GenericVector* data) { offset_ = 0; if (data != NULL) { if (data_is_owned_) delete data_; data_ = data; data_is_owned_ = false; } else if (!data_is_owned_) { data_ = new GenericVector; data_is_owned_ = true; } is_writing_ = true; data_->truncate(0); } bool TFile::CloseWrite(const STRING& filename, FileWriter writer) { ASSERT_HOST(is_writing_); if (writer == NULL) return SaveDataToFile(*data_, filename); else return (*writer)(*data_, filename); } int TFile::FWrite(const void* buffer, int size, int count) { ASSERT_HOST(is_writing_); int total = size * count; if (total <= 0) return 0; const char* buf = reinterpret_cast(buffer); // This isn't very efficient, but memory is so fast compared to disk // that it is relatively unimportant, and very simple. for (int i = 0; i < total; ++i) data_->push_back(buf[i]); return count; } } // namespace tesseract. tesseract-3.04.01/ccutil/serialis.h000066400000000000000000000071531266071204500171340ustar00rootroot00000000000000/********************************************************************** * File: serialis.h (Formerly serialmac.h) * Description: Inline routines and macros for serialisation functions * Author: Phil Cheatle * Created: Tue Oct 08 08:33:12 BST 1991 * * (C) Copyright 1990, 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 SERIALIS_H #define SERIALIS_H #include #include #include #include "host.h" template class GenericVector; class STRING; /*********************************************************************** QUOTE_IT MACRO DEFINITION =========================== Replace with "". may be an arbitrary number of tokens ***********************************************************************/ #define QUOTE_IT( parm ) #parm namespace tesseract { // Function to read a GenericVector from a whole file. // Returns false on failure. typedef bool (*FileReader)(const STRING& filename, GenericVector* data); // Function to write a GenericVector to a whole file. // Returns false on failure. typedef bool (*FileWriter)(const GenericVector& data, const STRING& filename); // Simple file class. // Allows for portable file input from memory and from foreign file systems. class TFile { public: TFile(); ~TFile(); // All the Open methods load the whole file into memory for reading. // Opens a file with a supplied reader, or NULL to use the default. // Note that mixed read/write is not supported. bool Open(const STRING& filename, FileReader reader); // From an existing memory buffer. bool Open(const char* data, int size); // From an open file and an end offset. bool Open(FILE* fp, inT64 end_offset); // Reads a line like fgets. Returns NULL on EOF, otherwise buffer. // Reads at most buffer_size bytes, including '\0' terminator, even if // the line is longer. Does nothing if buffer_size <= 0. // To use fscanf use FGets and sscanf. char* FGets(char* buffer, int buffer_size); // Replicates fread, returning the number of items read. int FRead(void* buffer, int size, int count); // Resets the TFile as if it has been Opened, but nothing read. // Only allowed while reading! void Rewind(); // Open for writing. Either supply a non-NULL data with OpenWrite before // calling FWrite, (no close required), or supply a NULL data to OpenWrite // and call CloseWrite to write to a file after the FWrites. void OpenWrite(GenericVector* data); bool CloseWrite(const STRING& filename, FileWriter writer); // Replicates fwrite, returning the number of items written. // To use fprintf, use snprintf and FWrite. int FWrite(const void* buffer, int size, int count); private: // The number of bytes used so far. int offset_; // The buffered data from the file. GenericVector* data_; // True if the data_ pointer is owned by *this. bool data_is_owned_; // True if the TFile is open for writing. bool is_writing_; }; } // namespace tesseract. #endif tesseract-3.04.01/ccutil/sorthelper.h000066400000000000000000000074151266071204500175110ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: sorthelper.h // Description: Generic sort and maxfinding class. // Author: Ray Smith // Created: Thu May 20 17:48:21 PDT 2010 // // (C) Copyright 2010, 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_SORTHELPER_H_ #define TESSERACT_CCUTIL_SORTHELPER_H_ #include #include "genericvector.h" // Generic class to provide functions based on a pair. // T is the value type. // The class keeps a count of each value and can return the most frequent // value or a sorted array of the values with counts. // Note that this class uses linear search for adding. It is better // to use the STATS class to get the mode of a large number of values // in a small space. SortHelper is better to get the mode of a small number // of values from a large space. // T must have a copy constructor. template class SortHelper { public: // Simple pair class to hold the values and counts. template struct SortPair { PairT value; int count; }; // qsort function to sort by decreasing count. static int SortPairsByCount(const void* v1, const void* v2) { const SortPair* p1 = reinterpret_cast*>(v1); const SortPair* p2 = reinterpret_cast*>(v2); return p2->count - p1->count; } // qsort function to sort by decreasing value. static int SortPairsByValue(const void* v1, const void* v2) { const SortPair* p1 = reinterpret_cast*>(v1); const SortPair* p2 = reinterpret_cast*>(v2); if (p2->value - p1->value < 0) return -1; if (p2->value - p1->value > 0) return 1; return 0; } // Constructor takes a hint of the array size, but it need not be accurate. explicit SortHelper(int sizehint) { counts_.reserve(sizehint); } // Add a value that may be a duplicate of an existing value. // Uses a linear search. void Add(T value, int count) { // Linear search for value. for (int i = 0; i < counts_.size(); ++i) { if (counts_[i].value == value) { counts_[i].count += count; return; } } SortPair new_pair = {value, count}; counts_.push_back(SortPair(new_pair)); } // Returns the frequency of the most frequent value. // If max_value is not NULL, returns the most frequent value. // If the array is empty, returns -MAX_INT32 and max_value is unchanged. int MaxCount(T* max_value) const { int best_count = -MAX_INT32; for (int i = 0; i < counts_.size(); ++i) { if (counts_[i].count > best_count) { best_count = counts_[i].count; if (max_value != NULL) *max_value = counts_[i].value; } } return best_count; } // Returns the data array sorted by decreasing frequency. const GenericVector >& SortByCount() { counts_.sort(&SortPairsByCount); return counts_; } // Returns the data array sorted by decreasing value. const GenericVector >& SortByValue() { counts_.sort(&SortPairsByValue); return counts_; } private: GenericVector > counts_; }; #endif // TESSERACT_CCUTIL_SORTHELPER_H_. tesseract-3.04.01/ccutil/stderr.h000066400000000000000000000020701266071204500166150ustar00rootroot00000000000000/********************************************************************** * File: stderr.h (Formerly stderrs.h) * Description: File defining error messages fundamental of commonly used. * Author: Ray Smith * Created: Fri Aug 10 15:23:14 BST 1990 * * (C) Copyright 1990, 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 STDERR_H #define STDERR_H #include "errcode.h" const ERRCODE MEMORY_OUT = "Out of memory"; #endif tesseract-3.04.01/ccutil/strngs.cpp000066400000000000000000000354471266071204500172030ustar00rootroot00000000000000/********************************************************************** * File: strngs.c (Formerly strings.c) * Description: STRING class functions. * Author: Ray Smith * Created: Fri Feb 15 09:13:30 GMT 1991 * * (C) Copyright 1991, 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. * **********************************************************************/ #include "strngs.h" #include #include "genericvector.h" #include "helpers.h" #include "serialis.h" #include "tprintf.h" using tesseract::TFile; // Size of buffer needed to host the decimal representation of the maximum // 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, * including total capacity and how much used (strlen with '\0'). * * The implementation hides this header at the start of the data * buffer and appends the string on the end to keep sizeof(STRING) * unchanged from earlier versions so serialization is not affected. * * The collection of MACROS provide different implementations depending * on whether the string keeps track of its strlen or not so that this * feature can be added in later when consumers don't modify the string **********************************************************************/ // Smallest string to allocate by default const int kMinCapacity = 16; char* STRING::AllocData(int used, int capacity) { data_ = (STRING_HEADER *)alloc_string(capacity + sizeof(STRING_HEADER)); // header is the metadata for this memory block STRING_HEADER* header = GetHeader(); header->capacity_ = capacity; header->used_ = used; return GetCStr(); } void STRING::DiscardData() { free_string((char *)data_); } // This is a private method; ensure FixHeader is called (or used_ is well defined) // beforehand char* STRING::ensure_cstr(inT32 min_capacity) { STRING_HEADER* orig_header = GetHeader(); if (min_capacity <= orig_header->capacity_) return ((char *)this->data_) + sizeof(STRING_HEADER); // if we are going to grow bigger, than double our existing // size, but if that still is not big enough then keep the // requested capacity if (min_capacity < 2 * orig_header->capacity_) min_capacity = 2 * orig_header->capacity_; int alloc = sizeof(STRING_HEADER) + min_capacity; STRING_HEADER* new_header = (STRING_HEADER*)(alloc_string(alloc)); memcpy(&new_header[1], GetCStr(), orig_header->used_); new_header->capacity_ = min_capacity; new_header->used_ = orig_header->used_; // free old memory, then rebind to new memory DiscardData(); data_ = new_header; assert(InvariantOk()); return ((char *)data_) + sizeof(STRING_HEADER); } // This is const, but is modifying a mutable field // this way it can be used on const or non-const instances. void STRING::FixHeader() const { const STRING_HEADER* header = GetHeader(); if (header->used_ < 0) header->used_ = strlen(GetCStr()) + 1; } STRING::STRING() { // Empty STRINGs contain just the "\0". memcpy(AllocData(1, kMinCapacity), "", 1); } STRING::STRING(const STRING& str) { str.FixHeader(); const STRING_HEADER* str_header = str.GetHeader(); int str_used = str_header->used_; char *this_cstr = AllocData(str_used, str_used); memcpy(this_cstr, str.GetCStr(), str_used); assert(InvariantOk()); } STRING::STRING(const char* cstr) { if (cstr == NULL) { // Empty STRINGs contain just the "\0". memcpy(AllocData(1, kMinCapacity), "", 1); } else { int len = strlen(cstr) + 1; char* this_cstr = AllocData(len, len); memcpy(this_cstr, cstr, len); } assert(InvariantOk()); } STRING::STRING(const char *data, int length) { if (data == NULL) { // Empty STRINGs contain just the "\0". memcpy(AllocData(1, kMinCapacity), "", 1); } else { char* this_cstr = AllocData(length + 1, length + 1); memcpy(this_cstr, data, length); this_cstr[length] = '\0'; } } STRING::~STRING() { DiscardData(); } // TODO(rays) Change all callers to use TFile and remove the old functions. // Writes to the given file. Returns false in case of error. bool STRING::Serialize(FILE* fp) const { inT32 len = length(); if (fwrite(&len, sizeof(len), 1, fp) != 1) return false; if (static_cast(fwrite(GetCStr(), 1, len, fp)) != len) return false; return true; } // Writes to the given file. Returns false in case of error. bool STRING::Serialize(TFile* fp) const { inT32 len = length(); if (fp->FWrite(&len, sizeof(len), 1) != 1) return false; if (fp->FWrite(GetCStr(), 1, len) != len) return false; return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool STRING::DeSerialize(bool swap, FILE* fp) { inT32 len; if (fread(&len, sizeof(len), 1, fp) != 1) return false; if (swap) ReverseN(&len, sizeof(len)); truncate_at(len); if (static_cast(fread(GetCStr(), 1, len, fp)) != len) return false; return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool STRING::DeSerialize(bool swap, TFile* fp) { inT32 len; if (fp->FRead(&len, sizeof(len), 1) != 1) return false; if (swap) ReverseN(&len, sizeof(len)); truncate_at(len); if (fp->FRead(GetCStr(), 1, len) != len) return false; return true; } BOOL8 STRING::contains(const char c) const { return (c != '\0') && (strchr (GetCStr(), c) != NULL); } inT32 STRING::length() const { FixHeader(); return GetHeader()->used_ - 1; } const char* STRING::string() const { const STRING_HEADER* header = GetHeader(); if (header->used_ == 0) return NULL; // mark header length unreliable because tesseract might // cast away the const and mutate the string directly. header->used_ = -1; 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 * without violating encapsulation. * * Also makes the [] operator return a const so it is immutable */ #if STRING_IS_PROTECTED const char& STRING::operator[](inT32 index) const { return GetCStr()[index]; } void STRING::insert_range(inT32 index, const char* str, int len) { // if index is outside current range, then also grow size of string // to accmodate the requested range. STRING_HEADER* this_header = GetHeader(); int used = this_header->used_; if (index > used) used = index; char* this_cstr = ensure_cstr(used + len + 1); if (index < used) { // move existing string from index to '\0' inclusive. memmove(this_cstr + index + len, this_cstr + index, this_header->used_ - index); } else if (len > 0) { // We are going to overwrite previous null terminator, so write the new one. this_cstr[this_header->used_ + len - 1] = '\0'; // If the old header did not have the terminator, // then we need to account for it now that we've added it. // Otherwise it was already accounted for; we just moved it. if (this_header->used_ == 0) ++this_header->used_; } // Write new string to index. // The string is already terminated from the conditions above. memcpy(this_cstr + index, str, len); this_header->used_ += len; assert(InvariantOk()); } void STRING::erase_range(inT32 index, int len) { char* this_cstr = GetCStr(); STRING_HEADER* this_header = GetHeader(); memcpy(this_cstr+index, this_cstr+index+len, this_header->used_ - index - len); this_header->used_ -= len; assert(InvariantOk()); } #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; assert(InvariantOk()); } char& STRING::operator[](inT32 index) const { // Code is casting away this const and mutating the string, // so mark used_ as -1 to flag it unreliable. GetHeader()->used_ = -1; return ((char *)GetCStr())[index]; } #endif void STRING::split(const char c, GenericVector *splited) { int start_index = 0; int len = length(); for (int i = 0; i < len; i++) { if ((*this)[i] == c) { if (i != start_index) { (*this)[i] = '\0'; splited->push_back(STRING(GetCStr() + start_index, i - start_index)); (*this)[i] = c; } start_index = i + 1; } } if (len != start_index) { splited->push_back(STRING(GetCStr() + start_index, len - start_index)); } } BOOL8 STRING::operator==(const STRING& str) const { FixHeader(); str.FixHeader(); const STRING_HEADER* str_header = str.GetHeader(); const STRING_HEADER* this_header = GetHeader(); int this_used = this_header->used_; int str_used = str_header->used_; return (this_used == str_used) && (memcmp(GetCStr(), str.GetCStr(), this_used) == 0); } BOOL8 STRING::operator!=(const STRING& str) const { FixHeader(); str.FixHeader(); const STRING_HEADER* str_header = str.GetHeader(); const STRING_HEADER* this_header = GetHeader(); int this_used = this_header->used_; int str_used = str_header->used_; return (this_used != str_used) || (memcmp(GetCStr(), str.GetCStr(), this_used) != 0); } BOOL8 STRING::operator!=(const char* cstr) const { FixHeader(); const STRING_HEADER* this_header = GetHeader(); if (cstr == NULL) return this_header->used_ > 1; // either '\0' or NULL else { inT32 length = strlen(cstr) + 1; return (this_header->used_ != length) || (memcmp(GetCStr(), cstr, length) != 0); } } STRING& STRING::operator=(const STRING& str) { str.FixHeader(); const STRING_HEADER* str_header = str.GetHeader(); int str_used = str_header->used_; GetHeader()->used_ = 0; // clear since ensure doesn't need to copy data char* this_cstr = ensure_cstr(str_used); STRING_HEADER* this_header = GetHeader(); memcpy(this_cstr, str.GetCStr(), str_used); this_header->used_ = str_used; assert(InvariantOk()); return *this; } STRING & STRING::operator+=(const STRING& str) { FixHeader(); str.FixHeader(); const STRING_HEADER* str_header = str.GetHeader(); const char* str_cstr = str.GetCStr(); int str_used = str_header->used_; int this_used = GetHeader()->used_; char* this_cstr = ensure_cstr(this_used + str_used); STRING_HEADER* this_header = GetHeader(); // after ensure for realloc if (this_used > 1) { memcpy(this_cstr + this_used - 1, str_cstr, str_used); this_header->used_ += str_used - 1; // overwrite '\0' } else { memcpy(this_cstr, str_cstr, str_used); this_header->used_ = str_used; } assert(InvariantOk()); return *this; } void STRING::add_str_int(const char* str, int number) { if (str != NULL) *this += str; // Allow space for the maximum possible length of inT64. char num_buffer[kMaxIntSize]; snprintf(num_buffer, kMaxIntSize - 1, "%d", 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(); if (cstr) { int len = strlen(cstr) + 1; this_header->used_ = 0; // don't bother copying data if need to realloc char* this_cstr = ensure_cstr(len); this_header = GetHeader(); // for realloc memcpy(this_cstr, cstr, len); this_header->used_ = len; } else { // Reallocate to same state as default constructor. DiscardData(); // Empty STRINGs contain just the "\0". memcpy(AllocData(1, kMinCapacity), "", 1); } assert(InvariantOk()); return *this; } void STRING::assign(const char *cstr, int len) { STRING_HEADER* this_header = GetHeader(); this_header->used_ = 0; // don't bother copying data if need to realloc char* this_cstr = ensure_cstr(len + 1); // +1 for '\0' this_header = GetHeader(); // for realloc memcpy(this_cstr, cstr, len); this_cstr[len] = '\0'; this_header->used_ = len + 1; assert(InvariantOk()); } STRING STRING::operator+(const STRING& str) const { STRING result(*this); result += str; assert(InvariantOk()); return result; } STRING STRING::operator+(const char ch) const { STRING result; FixHeader(); const STRING_HEADER* this_header = GetHeader(); int this_used = this_header->used_; char* result_cstr = result.ensure_cstr(this_used + 1); STRING_HEADER* result_header = result.GetHeader(); int result_used = result_header->used_; // copies '\0' but we'll overwrite that memcpy(result_cstr, GetCStr(), this_used); result_cstr[result_used] = ch; // overwrite old '\0' result_cstr[result_used + 1] = '\0'; // append on '\0' ++result_header->used_; assert(InvariantOk()); return result; } STRING& STRING::operator+=(const char *str) { if (!str || !*str) // empty string has no effect return *this; FixHeader(); int len = strlen(str) + 1; int this_used = GetHeader()->used_; char* this_cstr = ensure_cstr(this_used + len); STRING_HEADER* this_header = GetHeader(); // after ensure for realloc // if we had non-empty string then append overwriting old '\0' // otherwise replace if (this_used > 0) { memcpy(this_cstr + this_used - 1, str, len); this_header->used_ += len - 1; } else { memcpy(this_cstr, str, len); this_header->used_ = len; } assert(InvariantOk()); return *this; } STRING& STRING::operator+=(const char ch) { if (ch == '\0') return *this; FixHeader(); int this_used = GetHeader()->used_; char* this_cstr = ensure_cstr(this_used + 1); STRING_HEADER* this_header = GetHeader(); if (this_used > 0) --this_used; // undo old empty null if there was one this_cstr[this_used++] = ch; // append ch to end this_cstr[this_used++] = '\0'; // append '\0' after ch this_header->used_ = this_used; assert(InvariantOk()); return *this; } tesseract-3.04.01/ccutil/strngs.h000066400000000000000000000144211266071204500166350ustar00rootroot00000000000000/********************************************************************** * File: strngs.h (Formerly strings.h) * Description: STRING class definition. * Author: Ray Smith * Created: Fri Feb 15 09:15:01 GMT 1991 * * (C) Copyright 1991, 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 STRNGS_H #define STRNGS_H #include #include #include "platform.h" #include "memry.h" namespace tesseract { class TFile; } // namespace tesseract. // STRING_IS_PROTECTED means that string[index] = X is invalid // because you have to go through strings interface to modify it. // This allows the string to ensure internal integrity and maintain // its own string length. Unfortunately this is not possible because // STRINGS are used as direct-manipulation data buffers for things // like length arrays and many places cast away the const on string() // to mutate the string. Turning this off means that internally we // cannot assume we know the strlen. #define STRING_IS_PROTECTED 0 template class GenericVector; class TESS_API STRING { public: STRING(); STRING(const STRING &string); STRING(const char *string); STRING(const char *data, int length); ~STRING (); // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); // Writes to the given file. Returns false in case of error. bool Serialize(tesseract::TFile* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, tesseract::TFile* fp); BOOL8 contains(const char c) const; 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; return strncpy(new char[len], GetCStr(), len); } #if STRING_IS_PROTECTED const char &operator[] (inT32 index) const; // len is number of chars in s to insert starting at index in this string void insert_range(inT32 index, const char*s, int len); void erase_range(inT32 index, int len); #else char &operator[] (inT32 index) const; #endif void split(const char c, GenericVector *splited); void truncate_at(inT32 index); BOOL8 operator== (const STRING & string) const; BOOL8 operator!= (const STRING & string) const; BOOL8 operator!= (const char *string) const; STRING & operator= (const char *string); STRING & operator= (const STRING & string); STRING operator+ (const STRING & string) const; STRING operator+ (const char ch) const; STRING & operator+= (const char *string); STRING & operator+= (const STRING & string); STRING & operator+= (const char ch); // Assignment for strings which are not null-terminated. void assign(const char *cstr, int len); // Appends the given string and int (as a %d) to this. // += cannot be used for ints as there as a char += operator that would // 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 capacity but keep pointer encapsulated inline void ensure(inT32 min_capacity) { ensure_cstr(min_capacity); } private: typedef struct STRING_HEADER { // How much space was allocated in the string buffer for char data. int capacity_; // used_ is how much of the capacity is currently being used, // including a '\0' terminator. // // If used_ is 0 then string is NULL (not even the '\0') // else if used_ > 0 then it is strlen() + 1 (because it includes '\0') // else strlen is >= 0 (not NULL) but needs to be computed. // this condition is set when encapsulation is violated because // an API returned a mutable string. // // capacity_ - used_ = excess capacity that the string can grow // without reallocating mutable int used_; } STRING_HEADER; // To preserve the behavior of the old serialization, we only have space // for one pointer in this structure. So we are embedding a data structure // at the start of the storage that will hold additional state variables, // then storing the actual string contents immediately after. STRING_HEADER* data_; // returns the header part of the storage inline STRING_HEADER* GetHeader() { return data_; } inline const STRING_HEADER* GetHeader() const { return data_; } // returns the string data part of storage inline char* GetCStr() { return ((char *)data_) + sizeof(STRING_HEADER); }; inline const char* GetCStr() const { return ((const char *)data_) + sizeof(STRING_HEADER); }; inline bool InvariantOk() const { #if STRING_IS_PROTECTED return (GetHeader()->used_ == 0) ? (string() == NULL) : (GetHeader()->used_ == (strlen(string()) + 1)); #else return true; #endif } // Ensure string has requested capacity as optimization // to avoid unnecessary reallocations. // The return value is a cstr buffer with at least requested capacity char* ensure_cstr(inT32 min_capacity); void FixHeader() const; // make used_ non-negative, even if const char* AllocData(int used, int capacity); void DiscardData(); }; #endif tesseract-3.04.01/ccutil/tesscallback.h000066400000000000000000012761131266071204500177610ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: tesscallback.h // Description: classes and functions to replace pointer-to-functions // Author: Samuel Charron // // (C) Copyright 2006, 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 _TESS_CALLBACK_SPECIALIZATIONS_H #define _TESS_CALLBACK_SPECIALIZATIONS_H #include "host.h" // For NULL. struct TessCallbackUtils_ { static void FailIsRepeatable(const char* name); }; class TessClosure { public: virtual ~TessClosure() { } virtual void Run() = 0; }; template class TessResultCallback { public: virtual ~TessResultCallback() { } virtual R Run() = 0; }; template class _ConstTessMemberResultCallback_0_0 : public TessResultCallback { public: typedef TessResultCallback base; typedef R (T::*MemberSignature)() const; private: const T* object_; MemberSignature member_; public: inline _ConstTessMemberResultCallback_0_0( const T* object, MemberSignature member) : object_(object), member_(member) { } virtual R Run() { if (!del) { R result = (object_->*member_)(); return result; } else { R result = (object_->*member_)(); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; return result; } } }; template class _ConstTessMemberResultCallback_0_0 : public TessClosure { public: typedef TessClosure base; typedef void (T::*MemberSignature)() const; private: const T* object_; MemberSignature member_; public: inline _ConstTessMemberResultCallback_0_0( const T* object, MemberSignature member) : object_(object), member_(member) { } virtual void Run() { if (!del) { (object_->*member_)(); } else { (object_->*member_)(); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; } } }; #ifndef SWIG template inline typename _ConstTessMemberResultCallback_0_0::base* NewTessCallback( const T1* obj, R (T2::*member)() const) { return new _ConstTessMemberResultCallback_0_0( obj, member); } #endif #ifndef SWIG template inline typename _ConstTessMemberResultCallback_0_0::base* NewPermanentTessCallback( const T1* obj, R (T2::*member)() const) { return new _ConstTessMemberResultCallback_0_0( obj, member); } #endif template class _TessMemberResultCallback_0_0 : public TessResultCallback { public: typedef TessResultCallback base; typedef R (T::*MemberSignature)() ; private: T* object_; MemberSignature member_; public: inline _TessMemberResultCallback_0_0( T* object, MemberSignature member) : object_(object), member_(member) { } virtual R Run() { if (!del) { R result = (object_->*member_)(); return result; } else { R result = (object_->*member_)(); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; return result; } } }; template class _TessMemberResultCallback_0_0 : public TessClosure { public: typedef TessClosure base; typedef void (T::*MemberSignature)() ; private: T* object_; MemberSignature member_; public: inline _TessMemberResultCallback_0_0( T* object, MemberSignature member) : object_(object), member_(member) { } virtual void Run() { if (!del) { (object_->*member_)(); } else { (object_->*member_)(); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; } } }; #ifndef SWIG template inline typename _TessMemberResultCallback_0_0::base* NewTessCallback( T1* obj, R (T2::*member)() ) { return new _TessMemberResultCallback_0_0( obj, member); } #endif #ifndef SWIG template inline typename _TessMemberResultCallback_0_0::base* NewPermanentTessCallback( T1* obj, R (T2::*member)() ) { return new _TessMemberResultCallback_0_0( obj, member); } #endif template class _TessFunctionResultCallback_0_0 : public TessResultCallback { public: typedef TessResultCallback base; typedef R (*FunctionSignature)(); private: FunctionSignature function_; public: inline _TessFunctionResultCallback_0_0( FunctionSignature function) : function_(function) { } virtual R Run() { if (!del) { R result = (*function_)(); return result; } else { R result = (*function_)(); // zero out the pointer to ensure segfault if used again function_ = NULL; delete this; return result; } } }; template class _TessFunctionResultCallback_0_0 : public TessClosure { public: typedef TessClosure base; typedef void (*FunctionSignature)(); private: FunctionSignature function_; public: inline _TessFunctionResultCallback_0_0( FunctionSignature function) : function_(function) { } virtual void Run() { if (!del) { (*function_)(); } else { (*function_)(); // zero out the pointer to ensure segfault if used again function_ = NULL; delete this; } } }; template inline typename _TessFunctionResultCallback_0_0::base* NewTessCallback(R (*function)()) { return new _TessFunctionResultCallback_0_0(function); } template inline typename _TessFunctionResultCallback_0_0::base* 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; }; 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 _TessFunctionResultCallback_2_5 : public TessCallback5 { public: 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 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 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_3_5(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,A5 a5) { if (!del) { R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); return result; } else { 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; return result; } } }; template class _ConstTessMemberResultCallback_3_5 : public TessCallback5 { public: 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_3_5(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,A5 a5) { if (!del) { (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); } else { (object_->*member_)(p1_,p2_,p3_,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_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_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_3_5 : public TessResultCallback5 { public: 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_3_5( 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,A5 a5) { if (!del) { R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); return result; } else { 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; return result; } } }; template class _TessMemberResultCallback_3_5 : public TessCallback5 { public: 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_3_5( 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,A5 a5) { if (!del) { (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); } else { (object_->*member_)(p1_,p2_,p3_,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_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_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_3_5 : public TessResultCallback5 { public: 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_3_5(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,A5 a5) { if (!del) { R result = (*function_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); return result; } else { 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; return result; } } }; template class _TessFunctionResultCallback_3_5 : public TessCallback5 { public: 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_3_5(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,A5 a5) { if (!del) { (*function_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); } else { (*function_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again function_ = NULL; delete this; } } }; 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_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 _ConstTessMemberResultCallback_4_5 : public TessResultCallback5 { public: 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_4_5(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,A5 a5) { if (!del) { R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); return result; } else { 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; return result; } } }; template class _ConstTessMemberResultCallback_4_5 : public TessCallback5 { public: 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_4_5(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,A5 a5) { if (!del) { (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); } else { (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; } } }; #ifndef SWIG 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_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_4_5 : public TessResultCallback5 { public: 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_4_5( 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,A5 a5) { if (!del) { R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); return result; } else { 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; return result; } } }; template class _TessMemberResultCallback_4_5 : public TessCallback5 { public: 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_4_5( 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,A5 a5) { if (!del) { (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); } else { (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; } } }; #ifndef SWIG 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_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_4_5 : public TessResultCallback5 { public: 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_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,A3 a3,A4 a4,A5 a5) { if (!del) { R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); return result; } else { 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; return result; } } }; template class _TessFunctionResultCallback_4_5 : public TessCallback5 { public: 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_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,A3 a3,A4 a4,A5 a5) { if (!del) { (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); } else { (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again function_ = NULL; delete this; } } }; 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_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 _ConstTessMemberResultCallback_5_5 : public TessResultCallback5 { public: 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_5_5(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,A5 a5) { if (!del) { R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); return result; } else { 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; return result; } } }; template class _ConstTessMemberResultCallback_5_5 : public TessCallback5 { public: 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_5_5(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,A5 a5) { if (!del) { (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); } else { (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; } } }; #ifndef SWIG 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_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_5_5 : public TessResultCallback5 { public: 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_5_5( 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,A5 a5) { if (!del) { R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); return result; } else { 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; return result; } } }; template class _TessMemberResultCallback_5_5 : public TessCallback5 { public: 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_5_5( 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,A5 a5) { if (!del) { (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); } else { (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; } } }; #ifndef SWIG 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_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_5_5 : public TessResultCallback5 { public: 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_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,A4 a4,A5 a5) { if (!del) { R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); return result; } else { 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; return result; } } }; template class _TessFunctionResultCallback_5_5 : public TessCallback5 { public: 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_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,A4 a4,A5 a5) { if (!del) { (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); } else { (*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; } } }; 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_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); } template class _ConstTessMemberResultCallback_6_5 : public TessResultCallback5 { public: typedef TessResultCallback5 base; typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,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_; typename remove_reference::type p6_; public: 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,A4 a4,A5 a5) { if (!del) { R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); return result; } else { 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; return result; } } }; template class _ConstTessMemberResultCallback_6_5 : public TessCallback5 { public: typedef TessCallback5 base; typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,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_; typename remove_reference::type p6_; public: 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,A4 a4,A5 a5) { if (!del) { (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); } else { (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; } } }; #ifndef SWIG 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_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_6_5 : public TessResultCallback5 { public: 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_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,A4 a4,A5 a5) { if (!del) { R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); return result; } else { 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; return result; } } }; template class _TessMemberResultCallback_6_5 : public TessCallback5 { public: 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_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,A4 a4,A5 a5) { if (!del) { (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); } else { (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; } } }; #ifndef SWIG 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_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_6_5 : public TessResultCallback5 { public: 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_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,A4 a4,A5 a5) { if (!del) { R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); return result; } else { 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; return result; } } }; template class _TessFunctionResultCallback_6_5 : public TessCallback5 { public: 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_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,A4 a4,A5 a5) { if (!del) { (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); } else { (*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; } } }; 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_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 */ tesseract-3.04.01/ccutil/tessdatamanager.cpp000066400000000000000000000234111266071204500210120ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: tessdatamanager.cpp // Description: Functions to handle loading/combining tesseract data files. // Author: Daria Antonova // Created: Wed Jun 03 11:26:43 PST 2009 // // (C) Copyright 2009, 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. // /////////////////////////////////////////////////////////////////////// #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #endif #include "tessdatamanager.h" #include #include "helpers.h" #include "serialis.h" #include "strngs.h" #include "tprintf.h" #include "params.h" 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); tprintf("Please make sure the TESSDATA_PREFIX environment variable is set " "to the parent directory of your \"tessdata\" directory.\n"); return false; } fread(&actual_tessdata_num_entries_, sizeof(inT32), 1, data_file_); swap_ = (actual_tessdata_num_entries_ > kMaxNumTessdataEntries); if (swap_) { ReverseN(&actual_tessdata_num_entries_, sizeof(actual_tessdata_num_entries_)); } if (actual_tessdata_num_entries_ > TESSDATA_NUM_ENTRIES) { // For forward compatibility, truncate to the number we can handle. actual_tessdata_num_entries_ = TESSDATA_NUM_ENTRIES; } fread(offset_table_, sizeof(inT64), actual_tessdata_num_entries_, data_file_); if (swap_) { for (i = 0 ; i < actual_tessdata_num_entries_; ++i) { ReverseN(&offset_table_[i], sizeof(offset_table_[i])); } } if (debug_level_) { tprintf("TessdataManager loaded %d types of tesseract data files.\n", actual_tessdata_num_entries_); for (i = 0; i < actual_tessdata_num_entries_; ++i) { tprintf("Offset for type %d is %lld\n", i, offset_table_[i]); } } return true; } void TessdataManager::CopyFile(FILE *input_file, FILE *output_file, bool newline_end, inT64 num_bytes_to_copy) { if (num_bytes_to_copy == 0) return; int buffer_size = 1024; if (num_bytes_to_copy > 0 && buffer_size > num_bytes_to_copy) { buffer_size = num_bytes_to_copy; } inT64 num_bytes_copied = 0; char *chunk = new char[buffer_size]; int bytes_read; char last_char = 0x0; while ((bytes_read = fread(chunk, sizeof(char), buffer_size, input_file))) { fwrite(chunk, sizeof(char), bytes_read, output_file); last_char = chunk[bytes_read-1]; if (num_bytes_to_copy > 0) { num_bytes_copied += bytes_read; if (num_bytes_copied == num_bytes_to_copy) break; if (num_bytes_copied + buffer_size > num_bytes_to_copy) { buffer_size = num_bytes_to_copy - num_bytes_copied; } } } if (newline_end) ASSERT_HOST(last_char == '\n'); delete[] chunk; } bool TessdataManager::WriteMetadata(inT64 *offset_table, const char * language_data_path_prefix, FILE *output_file) { inT32 num_entries = TESSDATA_NUM_ENTRIES; bool result = true; if (fseek(output_file, 0, SEEK_SET) != 0 || fwrite(&num_entries, sizeof(inT32), 1, output_file) != 1 || fwrite(offset_table, sizeof(inT64), TESSDATA_NUM_ENTRIES, output_file) != TESSDATA_NUM_ENTRIES) { fclose(output_file); result = false; tprintf("WriteMetadata failed in TessdataManager!\n"); } else if (fclose(output_file)) { result = false; tprintf("WriteMetadata failed to close file!\n"); } else { tprintf("TessdataManager combined tesseract data files.\n"); for (int i = 0; i < TESSDATA_NUM_ENTRIES; ++i) { tprintf("Offset for type %2d (%s%-22s) is %lld\n", i, language_data_path_prefix, kTessdataFileSuffixes[i], offset_table[i]); } } return result; } bool TessdataManager::CombineDataFiles( const char *language_data_path_prefix, const char *output_filename) { int i; inT64 offset_table[TESSDATA_NUM_ENTRIES]; for (i = 0; i < TESSDATA_NUM_ENTRIES; ++i) offset_table[i] = -1; FILE *output_file = fopen(output_filename, "wb"); if (output_file == NULL) { tprintf("Error opening %s for writing\n", output_filename); return false; } // Leave some space for recording the offset_table. if (fseek(output_file, sizeof(inT32) + sizeof(inT64) * TESSDATA_NUM_ENTRIES, SEEK_SET)) { tprintf("Error seeking %s\n", output_filename); return false; } TessdataType type = TESSDATA_NUM_ENTRIES; bool text_file = false; FILE *file_ptr[TESSDATA_NUM_ENTRIES]; // Load individual tessdata components from files. for (i = 0; i < TESSDATA_NUM_ENTRIES; ++i) { ASSERT_HOST(TessdataTypeFromFileSuffix( kTessdataFileSuffixes[i], &type, &text_file)); STRING filename = language_data_path_prefix; filename += kTessdataFileSuffixes[i]; file_ptr[i] = fopen(filename.string(), "rb"); if (file_ptr[i] != NULL) { offset_table[type] = ftell(output_file); CopyFile(file_ptr[i], output_file, text_file, -1); fclose(file_ptr[i]); } } // Make sure that the required components are present. if (file_ptr[TESSDATA_UNICHARSET] == NULL) { tprintf("Error opening %sunicharset file\n", language_data_path_prefix); fclose(output_file); return false; } if (file_ptr[TESSDATA_INTTEMP] != NULL && (file_ptr[TESSDATA_PFFMTABLE] == NULL || file_ptr[TESSDATA_NORMPROTO] == NULL)) { tprintf("Error opening %spffmtable and/or %snormproto files" " while %sinttemp file was present\n", language_data_path_prefix, language_data_path_prefix, language_data_path_prefix); fclose(output_file); return false; } return WriteMetadata(offset_table, language_data_path_prefix, output_file); } bool TessdataManager::OverwriteComponents( const char *new_traineddata_filename, char **component_filenames, int num_new_components) { int i; inT64 offset_table[TESSDATA_NUM_ENTRIES]; TessdataType type = TESSDATA_NUM_ENTRIES; bool text_file = false; FILE *file_ptr[TESSDATA_NUM_ENTRIES]; for (i = 0; i < TESSDATA_NUM_ENTRIES; ++i) { offset_table[i] = -1; file_ptr[i] = NULL; } FILE *output_file = fopen(new_traineddata_filename, "wb"); if (output_file == NULL) { tprintf("Error opening %s for writing\n", new_traineddata_filename); return false; } // Leave some space for recording the offset_table. if (fseek(output_file, sizeof(inT32) + sizeof(inT64) * TESSDATA_NUM_ENTRIES, SEEK_SET)) { fclose(output_file); tprintf("Error seeking %s\n", new_traineddata_filename); return false; } // Open the files with the new components. for (i = 0; i < num_new_components; ++i) { if (TessdataTypeFromFileName(component_filenames[i], &type, &text_file)) file_ptr[type] = fopen(component_filenames[i], "rb"); } // Write updated data to the output traineddata file. for (i = 0; i < TESSDATA_NUM_ENTRIES; ++i) { if (file_ptr[i] != NULL) { // Get the data from the opened component file. offset_table[i] = ftell(output_file); CopyFile(file_ptr[i], output_file, kTessdataFileIsText[i], -1); fclose(file_ptr[i]); } else { // Get this data component from the loaded data file. if (SeekToStart(static_cast(i))) { offset_table[i] = ftell(output_file); CopyFile(data_file_, output_file, kTessdataFileIsText[i], GetEndOffset(static_cast(i)) - ftell(data_file_) + 1); } } } const char *language_data_path_prefix = strchr(new_traineddata_filename, '.'); return WriteMetadata(offset_table, language_data_path_prefix, output_file); } bool TessdataManager::TessdataTypeFromFileSuffix( const char *suffix, TessdataType *type, bool *text_file) { for (int i = 0; i < TESSDATA_NUM_ENTRIES; ++i) { if (strcmp(kTessdataFileSuffixes[i], suffix) == 0) { *type = static_cast(i); *text_file = kTessdataFileIsText[i]; return true; } } tprintf("TessdataManager can't determine which tessdata" " component is represented by %s\n", suffix); return false; } bool TessdataManager::TessdataTypeFromFileName( const char *filename, TessdataType *type, bool *text_file) { // Get the file suffix (extension) const char *suffix = strrchr(filename, '.'); if (suffix == NULL || *(++suffix) == '\0') return false; return TessdataTypeFromFileSuffix(suffix, type, text_file); } bool TessdataManager::ExtractToFile(const char *filename) { TessdataType type = TESSDATA_NUM_ENTRIES; bool text_file = false; ASSERT_HOST(tesseract::TessdataManager::TessdataTypeFromFileName( filename, &type, &text_file)); if (!SeekToStart(type)) return false; FILE *output_file = fopen(filename, "wb"); if (output_file == NULL) { tprintf("Error opening %s\n", filename); exit(1); } inT64 begin_offset = ftell(GetDataFilePtr()); inT64 end_offset = GetEndOffset(type); tesseract::TessdataManager::CopyFile( GetDataFilePtr(), output_file, text_file, end_offset - begin_offset + 1); fclose(output_file); return true; } } // namespace tesseract tesseract-3.04.01/ccutil/tessdatamanager.h000066400000000000000000000261471266071204500204700ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: tessdatamanager.h // Description: Functions to handle loading/combining tesseract data files. // Author: Daria Antonova // Created: Wed Jun 03 11:26:43 PST 2009 // // (C) Copyright 2009, 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_TESSDATAMANAGER_H_ #define TESSERACT_CCUTIL_TESSDATAMANAGER_H_ #include #include "host.h" #include "strngs.h" #include "tprintf.h" static const char kTrainedDataSuffix[] = "traineddata"; // When adding new tessdata types and file suffixes, please make sure to // update TessdataType enum, kTessdataFileSuffixes and kTessdataFileIsText. static const char kLangConfigFileSuffix[] = "config"; static const char kUnicharsetFileSuffix[] = "unicharset"; static const char kAmbigsFileSuffix[] = "unicharambigs"; static const char kBuiltInTemplatesFileSuffix[] = "inttemp"; static const char kBuiltInCutoffsFileSuffix[] = "pffmtable"; static const char kNormProtoFileSuffix[] = "normproto"; static const char kPuncDawgFileSuffix[] = "punc-dawg"; static const char kSystemDawgFileSuffix[] = "word-dawg"; static const char kNumberDawgFileSuffix[] = "number-dawg"; static const char kFreqDawgFileSuffix[] = "freq-dawg"; static const char kFixedLengthDawgsFileSuffix[] = "fixed-length-dawgs"; static const char kCubeUnicharsetFileSuffix[] = "cube-unicharset"; 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 kParamsModelFileSuffix[] = "params-model"; namespace tesseract { enum TessdataType { TESSDATA_LANG_CONFIG, // 0 TESSDATA_UNICHARSET, // 1 TESSDATA_AMBIGS, // 2 TESSDATA_INTTEMP, // 3 TESSDATA_PFFMTABLE, // 4 TESSDATA_NORMPROTO, // 5 TESSDATA_PUNC_DAWG, // 6 TESSDATA_SYSTEM_DAWG, // 7 TESSDATA_NUMBER_DAWG, // 8 TESSDATA_FREQ_DAWG, // 9 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_MODEL, // 16 TESSDATA_NUM_ENTRIES }; /** * kTessdataFileSuffixes[i] indicates the file suffix for * tessdata of type i (from TessdataType enum). */ static const char * const kTessdataFileSuffixes[] = { kLangConfigFileSuffix, // 0 kUnicharsetFileSuffix, // 1 kAmbigsFileSuffix, // 2 kBuiltInTemplatesFileSuffix, // 3 kBuiltInCutoffsFileSuffix, // 4 kNormProtoFileSuffix, // 5 kPuncDawgFileSuffix, // 6 kSystemDawgFileSuffix, // 7 kNumberDawgFileSuffix, // 8 kFreqDawgFileSuffix, // 9 kFixedLengthDawgsFileSuffix, // 10 // deprecated kCubeUnicharsetFileSuffix, // 11 kCubeSystemDawgFileSuffix, // 12 kShapeTableFileSuffix, // 13 kBigramDawgFileSuffix, // 14 kUnambigDawgFileSuffix, // 15 kParamsModelFileSuffix, // 16 }; /** * If kTessdataFileIsText[i] is true - the tessdata component * of type i (from TessdataType enum) is text, and is binary otherwise. */ static const bool kTessdataFileIsText[] = { true, // 0 true, // 1 true, // 2 false, // 3 true, // 4 true, // 5 false, // 6 false, // 7 false, // 8 false, // 9 false, // 10 // deprecated true, // 11 false, // 12 false, // 13 false, // 14 false, // 15 true, // 16 }; /** * TessdataType could be updated to contain more entries, however * we do not expect that number to be astronomically high. * In order to automatically detect endianness TessdataManager will * flip the bits if actual_tessdata_num_entries_ is larger than * kMaxNumTessdataEntries. */ static const int kMaxNumTessdataEntries = 1000; class TessdataManager { public: TessdataManager() { data_file_ = NULL; actual_tessdata_num_entries_ = 0; for (int i = 0; i < TESSDATA_NUM_ENTRIES; ++i) { offset_table_[i] = -1; } } ~TessdataManager() {} int DebugLevel() { return debug_level_; } /** * Opens the given data file and reads the offset table. * @return true on success. */ 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_; } /** * Returns false if there is no data of the given type. * Otherwise does a seek on the data_file_ to position the pointer * at the start of the data of the given type. */ inline bool SeekToStart(TessdataType tessdata_type) { if (debug_level_) { tprintf("TessdataManager: seek to offset %lld - start of tessdata" "type %d (%s))\n", offset_table_[tessdata_type], tessdata_type, kTessdataFileSuffixes[tessdata_type]); } if (offset_table_[tessdata_type] < 0) { return false; } else { ASSERT_HOST(fseek(data_file_, static_cast(offset_table_[tessdata_type]), SEEK_SET) == 0); return true; } } /** Returns the end offset for the given tesseract data file type. */ inline inT64 GetEndOffset(TessdataType tessdata_type) const { int index = tessdata_type + 1; while (index < actual_tessdata_num_entries_ && offset_table_[index] == -1) { ++index; // skip tessdata types not present in the combined file } if (debug_level_) { tprintf("TessdataManager: end offset for type %d is %lld\n", tessdata_type, (index == actual_tessdata_num_entries_) ? -1 : offset_table_[index]); } return (index == actual_tessdata_num_entries_) ? -1 : offset_table_[index] - 1; } /** Closes data_file_ (if it was opened by Init()). */ inline void End() { if (data_file_ != NULL) { fclose(data_file_); data_file_ = NULL; } } bool swap() const { return swap_; } /** Writes the number of entries and the given offset table to output_file. * Returns false on error. */ static bool WriteMetadata(inT64 *offset_table, const char *language_data_path_prefix, FILE *output_file); /** * Reads all the standard tesseract config and data files for a language * at the given path and bundles them up into one binary data file. * Returns true if the combined traineddata file was successfully written. */ static bool CombineDataFiles(const char *language_data_path_prefix, const char *output_filename); /** * Gets the individual components from the data_file_ with which the class was * initialized. Overwrites the components specified by component_filenames. * Writes the updated traineddata file to new_traineddata_filename. */ bool OverwriteComponents(const char *new_traineddata_filename, char **component_filenames, int num_new_components); /** * Extracts tessdata component implied by the name of the input file from * the combined traineddata loaded into TessdataManager. * Writes the extracted component to the file indicated by the file name. * E.g. if the filename given is somepath/somelang.unicharset, unicharset * will be extracted from the data loaded into the TessdataManager and will * be written to somepath/somelang.unicharset. * @return true if the component was successfully extracted, false if the * component was not present in the traineddata loaded into TessdataManager. */ bool ExtractToFile(const char *filename); /** * Copies data from the given input file to the output_file provided. * If num_bytes_to_copy is >= 0, only num_bytes_to_copy is copied from * the input file, otherwise all the data in the input file is copied. */ static void CopyFile(FILE *input_file, FILE *output_file, bool newline_end, inT64 num_bytes_to_copy); /** * Fills type with TessdataType of the tessdata component represented by the * given file name. E.g. tessdata/eng.unicharset -> TESSDATA_UNICHARSET. * Sets *text_file to true if the component is in text format (e.g. * unicharset, unichar ambigs, config, etc). * @return true if the tessdata component type could be determined * from the given file name. */ static bool TessdataTypeFromFileSuffix(const char *suffix, TessdataType *type, bool *text_file); /** * Tries to determine tessdata component file suffix from filename, * returns true on success. */ static bool TessdataTypeFromFileName(const char *filename, TessdataType *type, bool *text_file); private: /** * Opens the file whose name is a concatenation of language_data_path_prefix * and file_suffix. Returns a file pointer to the opened file. */ static FILE *GetFilePtr(const char *language_data_path_prefix, const char *file_suffix, bool text_file); /** * Each offset_table_[i] contains a file offset in the combined data file * where the data of TessdataFileType i is stored. */ inT64 offset_table_[TESSDATA_NUM_ENTRIES]; /** * Actual number of entries in the tessdata table. This value can only be * same or smaller than TESSDATA_NUM_ENTRIES, but can never be larger, * since then it would be impossible to interpret the type of tessdata at * indices same and higher than TESSDATA_NUM_ENTRIES. * This parameter is used to allow for backward compatibility * 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. bool swap_; }; } // namespace tesseract #endif // TESSERACT_CCUTIL_TESSDATAMANAGER_H_ tesseract-3.04.01/ccutil/tprintf.cpp000066400000000000000000000047141266071204500173420ustar00rootroot00000000000000/********************************************************************** * File: tprintf.c * Description: Trace version of printf - portable between UX and NT * Author: Phil Cheatle * Created: Wed Jun 28 15:01:15 BST 1995 * * (C) Copyright 1995, 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. * **********************************************************************/ // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include #include #include "ccutil.h" #include "params.h" #include "strngs.h" #include "tprintf.h" #define MAX_MSG_LEN 65536 #define EXTERN // Since tprintf is protected by a mutex, these parameters can remain global. DLLSYM STRING_VAR(debug_file, "", "File to send tprintf output to"); DLLSYM void tprintf_internal( // Trace printf const char *format, ... // Message ) { tesseract::tprintfMutex.Lock(); 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 // Format into msg #ifdef _WIN32 offset += _vsnprintf(msg + offset, MAX_MSG_LEN - offset, format, args); if (strcmp(debug_file.string(), "/dev/null") == 0) debug_file.set_value("nul"); #else 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) { fclose(debugfp); debugfp = NULL; } if (debugfp != NULL) fprintf(debugfp, "%s", msg); else fprintf(stderr, "%s", msg); tesseract::tprintfMutex.Unlock(); } tesseract-3.04.01/ccutil/tprintf.h000066400000000000000000000027141266071204500170050ustar00rootroot00000000000000/********************************************************************** * File: tprintf.h * Description: Trace version of printf - portable between UX and NT * Author: Phil Cheatle * Created: Wed Jun 28 15:01:15 BST 1995 * * (C) Copyright 1995, 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 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(...) tprintf_internal(__VA_ARGS__) extern TESS_API void tprintf_internal( // Trace printf const char *format, ...); // Message #endif // define TESSERACT_CCUTIL_TPRINTF_H tesseract-3.04.01/ccutil/unichar.cpp000066400000000000000000000154441266071204500173070ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: unichar.cpp // Description: Unicode character/ligature class. // Author: Ray Smith // Created: Wed Jun 28 17:05:01 PDT 2006 // // (C) Copyright 2006, 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 "unichar.h" #include "errcode.h" #include "genericvector.h" #include "tprintf.h" #define UNI_MAX_LEGAL_UTF32 0x0010FFFF // Construct from a utf8 string. If len<0 then the string is null terminated. // If the string is too long to fit in the UNICHAR then it takes only what // will fit. Checks for illegal input and stops at an illegal sequence. // The resulting UNICHAR may be empty. UNICHAR::UNICHAR(const char* utf8_str, int len) { int total_len = 0; int step = 0; if (len < 0) { for (len = 0; len < UNICHAR_LEN && utf8_str[len] != 0; ++len); } for (total_len = 0; total_len < len; total_len += step) { step = utf8_step(utf8_str + total_len); if (total_len + step > UNICHAR_LEN) break; // Too long. if (step == 0) break; // Illegal first byte. int i; for (i = 1; i < step; ++i) if ((utf8_str[total_len + i] & 0xc0) != 0x80) break; if (i < step) break; // Illegal surrogate } memcpy(chars, utf8_str, total_len); if (total_len < UNICHAR_LEN) { chars[UNICHAR_LEN - 1] = total_len; while (total_len < UNICHAR_LEN - 1) chars[total_len++] = 0; } } // Construct from a single UCS4 character. Illegal values are ignored, // resulting in an empty UNICHAR. UNICHAR::UNICHAR(int unicode) { const int bytemask = 0xBF; const int bytemark = 0x80; if (unicode < 0x80) { chars[UNICHAR_LEN - 1] = 1; chars[2] = 0; chars[1] = 0; chars[0] = static_cast(unicode); } else if (unicode < 0x800) { chars[UNICHAR_LEN - 1] = 2; chars[2] = 0; chars[1] = static_cast((unicode | bytemark) & bytemask); unicode >>= 6; chars[0] = static_cast(unicode | 0xc0); } else if (unicode < 0x10000) { chars[UNICHAR_LEN - 1] = 3; chars[2] = static_cast((unicode | bytemark) & bytemask); unicode >>= 6; chars[1] = static_cast((unicode | bytemark) & bytemask); unicode >>= 6; chars[0] = static_cast(unicode | 0xe0); } else if (unicode <= UNI_MAX_LEGAL_UTF32) { chars[UNICHAR_LEN - 1] = 4; chars[3] = static_cast((unicode | bytemark) & bytemask); unicode >>= 6; chars[2] = static_cast((unicode | bytemark) & bytemask); unicode >>= 6; chars[1] = static_cast((unicode | bytemark) & bytemask); unicode >>= 6; chars[0] = static_cast(unicode | 0xf0); } else { memset(chars, 0, UNICHAR_LEN); } } // Get the first character as UCS-4. int UNICHAR::first_uni() const { static const int utf8_offsets[5] = { 0, 0, 0x3080, 0xE2080, 0x3C82080 }; int uni = 0; int len = utf8_step(chars); const char* src = chars; switch (len) { default: break; case 4: uni += static_cast(*src++); uni <<= 6; case 3: uni += static_cast(*src++); uni <<= 6; case 2: uni += static_cast(*src++); uni <<= 6; case 1: uni += static_cast(*src++); } uni -= utf8_offsets[len]; return uni; } // Get a terminated UTF8 string: Must delete[] it after use. char* UNICHAR::utf8_str() const { int len = utf8_len(); char* str = new char[len + 1]; memcpy(str, chars, len); str[len] = 0; return str; } // Get the number of bytes in the first character of the given utf8 string. int UNICHAR::utf8_step(const char* utf8_str) { static const char utf8_bytes[256] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0 }; return utf8_bytes[static_cast(*utf8_str)]; } UNICHAR::const_iterator& UNICHAR::const_iterator::operator++() { ASSERT_HOST(it_ != NULL); int step = utf8_step(it_); if (step == 0) { tprintf("ERROR: Illegal UTF8 encountered.\n"); for (int i = 0; i < 5 && it_[i] != '\0'; ++i) { tprintf("Index %d char = 0x%x\n", i, it_[i]); } step = 1; } it_ += step; return *this; } int UNICHAR::const_iterator::operator*() const { ASSERT_HOST(it_ != NULL); const int len = utf8_step(it_); if (len == 0) { tprintf("WARNING: Illegal UTF8 encountered\n"); return ' '; } UNICHAR uch(it_, len); return uch.first_uni(); } int UNICHAR::const_iterator::get_utf8(char* utf8_output) const { ASSERT_HOST(it_ != NULL); const int len = utf8_step(it_); if (len == 0) { tprintf("WARNING: Illegal UTF8 encountered\n"); utf8_output[0] = ' '; return 1; } strncpy(utf8_output, it_, len); return len; } int UNICHAR::const_iterator::utf8_len() const { ASSERT_HOST(it_ != NULL); const int len = utf8_step(it_); if (len == 0) { tprintf("WARNING: Illegal UTF8 encountered\n"); return 1; } return len; } bool UNICHAR::const_iterator::is_legal() const { return utf8_step(it_) > 0; } UNICHAR::const_iterator UNICHAR::begin(const char* utf8_str, const int len) { return UNICHAR::const_iterator(utf8_str); } UNICHAR::const_iterator UNICHAR::end(const char* utf8_str, const int len) { return UNICHAR::const_iterator(utf8_str + len); } // Converts a utf-8 string to a vector of unicodes. // Returns false if the input contains invalid UTF-8, and replaces // the rest of the string with a single space. bool UNICHAR::UTF8ToUnicode(const char* utf8_str, GenericVector* unicodes) { const int utf8_length = strlen(utf8_str); const_iterator end_it(end(utf8_str, utf8_length)); for (const_iterator it(begin(utf8_str, utf8_length)); it != end_it; ++it) { if (it.is_legal()) { unicodes->push_back(*it); } else { unicodes->push_back(' '); return false; } } return true; } tesseract-3.04.01/ccutil/unichar.h000066400000000000000000000141341266071204500167470ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: unichar.h // Description: Unicode character/ligature class. // Author: Ray Smith // Created: Wed Jun 28 17:05:01 PDT 2006 // // (C) Copyright 2006, 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_UNICHAR_H__ #define TESSERACT_CCUTIL_UNICHAR_H__ #include #include template class GenericVector; // Maximum number of characters that can be stored in a UNICHAR. Must be // at least 4. Must not exceed 31 without changing the coding of length. #define UNICHAR_LEN 30 // A UNICHAR_ID is the unique id of a unichar. typedef int UNICHAR_ID; // A variable to indicate an invalid or uninitialized unichar id. static const int INVALID_UNICHAR_ID = -1; // A special unichar that corresponds to INVALID_UNICHAR_ID. static const char INVALID_UNICHAR[] = "__INVALID_UNICHAR__"; enum StrongScriptDirection { DIR_NEUTRAL = 0, // Text contains only neutral characters. DIR_LEFT_TO_RIGHT = 1, // Text contains no Right-to-Left characters. DIR_RIGHT_TO_LEFT = 2, // Text contains no Left-to-Right characters. DIR_MIX = 3, // Text contains a mixture of left-to-right // and right-to-left characters. }; // The UNICHAR class holds a single classification result. This may be // a single Unicode character (stored as between 1 and 4 utf8 bytes) or // multple Unicode characters representing the NFKC expansion of a ligature // such as fi, ffl etc. These are also stored as utf8. class UNICHAR { public: UNICHAR() { memset(chars, 0, UNICHAR_LEN); } // Construct from a utf8 string. If len<0 then the string is null terminated. // If the string is too long to fit in the UNICHAR then it takes only what // will fit. UNICHAR(const char* utf8_str, int len); // Construct from a single UCS4 character. explicit UNICHAR(int unicode); // Default copy constructor and operator= are OK. // Get the first character as UCS-4. int first_uni() const; // Get the length of the UTF8 string. int utf8_len() const { int len = chars[UNICHAR_LEN - 1]; return len >=0 && len < UNICHAR_LEN ? len : UNICHAR_LEN; } // Get a UTF8 string, but NOT NULL terminated. const char* utf8() const { return chars; } // Get a terminated UTF8 string: Must delete[] it after use. char* utf8_str() const; // Get the number of bytes in the first character of the given utf8 string. static int utf8_step(const char* utf8_str); // A class to simplify iterating over and accessing elements of a UTF8 // string. Note that unlike the UNICHAR class, const_iterator does NOT COPY or // take ownership of the underlying byte array. It also does not permit // modification of the array (as the name suggests). // // Example: // for (UNICHAR::const_iterator it = UNICHAR::begin(str, str_len); // it != UNICHAR::end(str, len); // ++it) { // tprintf("UCS-4 symbol code = %d\n", *it); // char buf[5]; // int char_len = it.get_utf8(buf); buf[char_len] = '\0'; // tprintf("Char = %s\n", buf); // } class const_iterator { typedef const_iterator CI; public: // Step to the next UTF8 character. // If the current position is at an illegal UTF8 character, then print an // error message and step by one byte. If the current position is at a NULL // value, don't step past it. const_iterator& operator++(); // Return the UCS-4 value at the current position. // If the current position is at an illegal UTF8 value, return a single // space character. int operator*() const; // Store the UTF-8 encoding of the current codepoint into buf, which must be // at least 4 bytes long. Return the number of bytes written. // If the current position is at an illegal UTF8 value, writes a single // space character and returns 1. // Note that this method does not null-terminate the buffer. int get_utf8(char* buf) const; // Returns the number of bytes of the current codepoint. Returns 1 if the // current position is at an illegal UTF8 value. int utf8_len() const; // Returns true if the UTF-8 encoding at the current position is legal. bool is_legal() const; // Return the pointer into the string at the current position. const char* utf8_data() const { return it_; } // Iterator equality operators. friend bool operator==(const CI& lhs, const CI& rhs) { return lhs.it_ == rhs.it_; } friend bool operator!=(const CI& lhs, const CI& rhs) { return !(lhs == rhs); } private: friend class UNICHAR; explicit const_iterator(const char* it) : it_(it) {} const char* it_; // Pointer into the string. }; // Create a start/end iterator pointing to a string. Note that these methods // are static and do NOT create a copy or take ownership of the underlying // array. static const_iterator begin(const char* utf8_str, const int byte_length); static const_iterator end(const char* utf8_str, const int byte_length); // Converts a utf-8 string to a vector of unicodes. // Returns false if the input contains invalid UTF-8, and replaces // the rest of the string with a single space. static bool UTF8ToUnicode(const char* utf8_str, GenericVector* unicodes); private: // A UTF-8 representation of 1 or more Unicode characters. // The last element (chars[UNICHAR_LEN - 1]) is a length if // its value < UNICHAR_LEN, otherwise it is a genuine character. char chars[UNICHAR_LEN]; }; #endif // TESSERACT_CCUTIL_UNICHAR_H__ tesseract-3.04.01/ccutil/unicharmap.cpp000066400000000000000000000135371266071204500200060ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: unicharmap.cpp // Description: Unicode character/ligature to integer id class. // Author: Thomas Kielbus // Created: Wed Jun 28 17:05:01 PDT 2006 // // (C) Copyright 2006, 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 "unichar.h" #include "host.h" #include "unicharmap.h" UNICHARMAP::UNICHARMAP() : nodes(0) { } UNICHARMAP::~UNICHARMAP() { if (nodes != 0) delete[] nodes; } // Search the given unichar representation in the tree. Each character in the // string is interpreted as an index in an array of nodes. UNICHAR_ID UNICHARMAP::unichar_to_id(const char* const unichar_repr) const { const char* current_char = unichar_repr; UNICHARMAP_NODE* current_nodes = nodes; assert(*unichar_repr != '\0'); do { if (*(current_char + 1) == '\0') return current_nodes[static_cast(*current_char)].id; current_nodes = current_nodes[static_cast(*current_char)].children; ++current_char; } while (true); } // Search the given unichar representation in the tree, using length characters // from it maximum. Each character in the string is interpreted as an index in // an array of nodes. UNICHAR_ID UNICHARMAP::unichar_to_id(const char* const unichar_repr, int length) const { const char* current_char = unichar_repr; UNICHARMAP_NODE* current_nodes = nodes; assert(*unichar_repr != '\0'); assert(length > 0 && length <= UNICHAR_LEN); do { if (length == 1 || *(current_char + 1) == '\0') return current_nodes[static_cast(*current_char)].id; current_nodes = current_nodes[static_cast(*current_char)].children; ++current_char; --length; } while (true); } // Search the given unichar representation in the tree, creating the possibly // missing nodes. Once the right place has been found, insert the given id and // update the inserted flag to keep track of the insert. Each character in the // string is interpreted as an index in an array of nodes. void UNICHARMAP::insert(const char* const unichar_repr, UNICHAR_ID id) { const char* current_char = unichar_repr; UNICHARMAP_NODE** current_nodes_pointer = &nodes; assert(*unichar_repr != '\0'); assert(id >= 0); do { if (*current_nodes_pointer == 0) *current_nodes_pointer = new UNICHARMAP_NODE[256]; if (*(current_char + 1) == '\0') { (*current_nodes_pointer) [static_cast(*current_char)].id = id; return; } current_nodes_pointer = &((*current_nodes_pointer) [static_cast(*current_char)].children); ++current_char; } while (true); } // Search the given unichar representation in the tree. Each character in the // string is interpreted as an index in an array of nodes. Stop once the tree // does not have anymore nodes or once we found the right unichar_repr. bool UNICHARMAP::contains(const char* const unichar_repr) const { if (unichar_repr == NULL || *unichar_repr == '\0') return false; const char* current_char = unichar_repr; UNICHARMAP_NODE* current_nodes = nodes; while (current_nodes != 0 && *(current_char + 1) != '\0') { current_nodes = current_nodes[static_cast(*current_char)].children; ++current_char; } return current_nodes != 0 && *(current_char + 1) == '\0' && current_nodes[static_cast(*current_char)].id >= 0; } // Search the given unichar representation in the tree, using length characters // from it maximum. Each character in the string is interpreted as an index in // an array of nodes. Stop once the tree does not have anymore nodes or once we // found the right unichar_repr. bool UNICHARMAP::contains(const char* const unichar_repr, int length) const { if (unichar_repr == NULL || *unichar_repr == '\0') return false; if (length <= 0 || length > UNICHAR_LEN) return false; const char* current_char = unichar_repr; UNICHARMAP_NODE* current_nodes = nodes; while (current_nodes != 0 && (length > 1 && *(current_char + 1) != '\0')) { current_nodes = current_nodes[static_cast(*current_char)].children; --length; ++current_char; } return current_nodes != 0 && (length == 1 || *(current_char + 1) == '\0') && current_nodes[static_cast(*current_char)].id >= 0; } // Return the minimum number of characters that must be used from this string // to obtain a match in the UNICHARMAP. int UNICHARMAP::minmatch(const char* const unichar_repr) const { const char* current_char = unichar_repr; UNICHARMAP_NODE* current_nodes = nodes; while (current_nodes != NULL && *current_char != '\0') { if (current_nodes[static_cast(*current_char)].id >= 0) return current_char + 1 - unichar_repr; current_nodes = current_nodes[static_cast(*current_char)].children; ++current_char; } return 0; } void UNICHARMAP::clear() { if (nodes != 0) { delete[] nodes; nodes = 0; } } UNICHARMAP::UNICHARMAP_NODE::UNICHARMAP_NODE() : children(0), id(-1) { } // Recursively delete the children UNICHARMAP::UNICHARMAP_NODE::~UNICHARMAP_NODE() { if (children != 0) { delete[] children; } } tesseract-3.04.01/ccutil/unicharmap.h000066400000000000000000000056571266071204500174570ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: unicharmap.h // Description: Unicode character/ligature to integer id class. // Author: Thomas Kielbus // Created: Wed Jun 28 17:05:01 PDT 2006 // // (C) Copyright 2006, 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_UNICHARMAP_H__ #define TESSERACT_CCUTIL_UNICHARMAP_H__ #include "unichar.h" // A UNICHARMAP stores unique unichars. Each of them is associated with one // UNICHAR_ID. class UNICHARMAP { public: // Create an empty UNICHARMAP UNICHARMAP(); ~UNICHARMAP(); // Insert the given unichar represention in the UNICHARMAP and associate it // with the given id. The length of the representation MUST be non-zero. void insert(const char* const unichar_repr, UNICHAR_ID id); // Return the id associated with the given unichar representation, // this representation MUST exist within the UNICHARMAP. // The length of the representation MUST be non-zero. UNICHAR_ID unichar_to_id(const char* const unichar_repr) const; // Return the id associated with the given unichar representation, // this representation MUST exist within the UNICHARMAP. The first // length characters (maximum) from unichar_repr are used. The length // MUST be non-zero. UNICHAR_ID unichar_to_id(const char* const unichar_repr, int length) const; // Return true if the given unichar representation is already present in the // UNICHARMAP. The length of the representation MUST be non-zero. bool contains(const char* const unichar_repr) const; // Return true if the given unichar representation is already present in the // UNICHARMAP. The first length characters (maximum) from unichar_repr are // used. The length MUST be non-zero. bool contains(const char* const unichar_repr, int length) const; // Return the minimum number of characters that must be used from this string // to obtain a match in the UNICHARMAP. int minmatch(const char* const unichar_repr) const; // Clear the UNICHARMAP. All previous data is lost. void clear(); private: // The UNICHARMAP is represented as a tree whose nodes are of type // UNICHARMAP_NODE. struct UNICHARMAP_NODE { UNICHARMAP_NODE(); ~UNICHARMAP_NODE(); UNICHARMAP_NODE* children; UNICHAR_ID id; }; UNICHARMAP_NODE* nodes; }; #endif // TESSERACT_CCUTIL_UNICHARMAP_H__ tesseract-3.04.01/ccutil/unicharset.cpp000066400000000000000000001162021266071204500200150ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: unicharset.cpp // Description: Unicode character/ligature set class. // Author: Thomas Kielbus // Created: Wed Jun 28 17:05:01 PDT 2006 // // (C) Copyright 2006, 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 "unicharset.h" #include #include #include #include "params.h" #include "serialis.h" #include "tesscallback.h" #include "tprintf.h" #include "unichar.h" // Special character used in representing character fragments. static const char kSeparator = '|'; // Special character used in representing 'natural' character fragments. static const char kNaturalFlag = 'n'; static const int ISALPHA_MASK = 0x1; static const int ISLOWER_MASK = 0x2; static const int ISUPPER_MASK = 0x4; static const int ISDIGIT_MASK = 0x8; static const int ISPUNCTUATION_MASK = 0x10; // Y coordinate threshold for determining cap-height vs x-height. // TODO(rays) Bring the global definition down to the ccutil library level, // so this constant is relative to some other constants. static const int kMeanlineThreshold = 220; // Let C be the number of alpha chars for which all tops exceed // kMeanlineThreshold, and X the number of alpha chars for which all // tops are below kMeanlineThreshold, then if X > C * // kMinXHeightFraction and C > X * kMinCapHeightFraction or more than // half the alpha characters have upper or lower case, then the // unicharset "has x-height". const double kMinXHeightFraction = 0.25; const double kMinCapHeightFraction = 0.05; /*static */ const char* UNICHARSET::kCustomLigatures[][2] = { {"ct", "\uE003"}, // c + t -> U+E003 {"ſh", "\uE006"}, // long-s + h -> U+E006 {"ſi", "\uE007"}, // long-s + i -> U+E007 {"ſl", "\uE008"}, // long-s + l -> U+E008 {"ſſ", "\uE009"}, // long-s + long-s -> U+E009 {NULL, NULL} }; // List of strings for the SpecialUnicharCodes. Keep in sync with the enum. const char* UNICHARSET::kSpecialUnicharCodes[SPECIAL_UNICHAR_CODES_COUNT] = { " ", "Joined", "|Broken|0|1" }; UNICHARSET::UNICHAR_PROPERTIES::UNICHAR_PROPERTIES() { Init(); } // Initialize all properties to sensible default values. void UNICHARSET::UNICHAR_PROPERTIES::Init() { isalpha = false; islower = false; isupper = false; isdigit = false; ispunctuation = false; isngram = false; enabled = false; SetRangesOpen(); script_id = 0; other_case = 0; mirror = 0; normed = ""; direction = UNICHARSET::U_LEFT_TO_RIGHT; fragment = NULL; } // Sets all ranges wide open. Initialization default in case there are // no useful values available. void UNICHARSET::UNICHAR_PROPERTIES::SetRangesOpen() { min_bottom = 0; max_bottom = MAX_UINT8; min_top = 0; max_top = MAX_UINT8; width = 0.0f; width_sd = 0.0f; bearing = 0.0f; bearing_sd = 0.0f; advance = 0.0f; advance_sd = 0.0f; } // Sets all ranges to empty. Used before expanding with font-based data. void UNICHARSET::UNICHAR_PROPERTIES::SetRangesEmpty() { min_bottom = MAX_UINT8; max_bottom = 0; min_top = MAX_UINT8; max_top = 0; width = 0.0f; width_sd = 0.0f; bearing = 0.0f; bearing_sd = 0.0f; advance = 0.0f; advance_sd = 0.0f; } // Returns true if any of the top/bottom/width/bearing/advance ranges/stats // is emtpy. bool UNICHARSET::UNICHAR_PROPERTIES::AnyRangeEmpty() const { return width == 0.0f || advance == 0.0f; } // Expands the ranges with the ranges from the src properties. void UNICHARSET::UNICHAR_PROPERTIES::ExpandRangesFrom( const UNICHAR_PROPERTIES& src) { UpdateRange(src.min_bottom, &min_bottom, &max_bottom); UpdateRange(src.max_bottom, &min_bottom, &max_bottom); UpdateRange(src.min_top, &min_top, &max_top); UpdateRange(src.max_top, &min_top, &max_top); if (src.width_sd > width_sd) { width = src.width; width_sd = src.width_sd; } if (src.bearing_sd > bearing_sd) { bearing = src.bearing; bearing_sd = src.bearing_sd; } if (src.advance_sd > advance_sd) { advance = src.advance; advance_sd = src.advance_sd; } } // Copies the properties from src into this. void UNICHARSET::UNICHAR_PROPERTIES::CopyFrom(const UNICHAR_PROPERTIES& src) { // Apart from the fragment, everything else can be done with a default copy. CHAR_FRAGMENT* saved_fragment = fragment; *this = src; // Bitwise copy. fragment = saved_fragment; } UNICHARSET::UNICHARSET() : unichars(NULL), ids(), size_used(0), size_reserved(0), script_table(NULL), script_table_size_used(0), null_script("NULL") { clear(); for (int i = 0; i < SPECIAL_UNICHAR_CODES_COUNT; ++i) { unichar_insert(kSpecialUnicharCodes[i]); if (i == UNICHAR_JOINED) set_isngram(i, true); } } UNICHARSET::~UNICHARSET() { clear(); } void UNICHARSET::reserve(int unichars_number) { if (unichars_number > size_reserved) { UNICHAR_SLOT* unichars_new = new UNICHAR_SLOT[unichars_number]; for (int i = 0; i < size_used; ++i) unichars_new[i] = unichars[i]; for (int j = size_used; j < unichars_number; ++j) { unichars_new[j].properties.script_id = add_script(null_script); } delete[] unichars; unichars = unichars_new; size_reserved = unichars_number; } } UNICHAR_ID UNICHARSET::unichar_to_id(const char* const unichar_repr) const { return ids.contains(unichar_repr) ? ids.unichar_to_id(unichar_repr) : INVALID_UNICHAR_ID; } UNICHAR_ID UNICHARSET::unichar_to_id(const char* const unichar_repr, int length) const { assert(length > 0 && length <= UNICHAR_LEN); return ids.contains(unichar_repr, length) ? ids.unichar_to_id(unichar_repr, length) : INVALID_UNICHAR_ID; } // Return the minimum number of bytes that matches a legal UNICHAR_ID, // while leaving the rest of the string encodable. Returns 0 if the // beginning of the string is not encodable. // WARNING: this function now encodes the whole string for precision. // Use encode_string in preference to repeatedly calling step. int UNICHARSET::step(const char* str) const { GenericVector encoding; GenericVector lengths; encode_string(str, true, &encoding, &lengths, NULL); if (encoding.empty() || encoding[0] == INVALID_UNICHAR_ID) return 0; return lengths[0]; } // Return whether the given UTF-8 string is encodable with this UNICHARSET. // If not encodable, write the first byte offset which cannot be converted // into the second (return) argument. bool UNICHARSET::encodable_string(const char *str, int *first_bad_position) const { GenericVector encoding; return encode_string(str, true, &encoding, NULL, first_bad_position); } // Encodes the given UTF-8 string with this UNICHARSET. // Returns true if the encoding succeeds completely, false if there is at // least one INVALID_UNICHAR_ID in the returned encoding, but in this case // the rest of the string is still encoded. // If lengths is not NULL, then it is filled with the corresponding // byte length of each encoded UNICHAR_ID. bool UNICHARSET::encode_string(const char* str, bool give_up_on_failure, GenericVector* encoding, GenericVector* lengths, int* encoded_length) const { GenericVector working_encoding; GenericVector working_lengths; GenericVector best_lengths; encoding->truncate(0); // Just in case str is empty. int str_length = strlen(str); int str_pos = 0; bool perfect = true; while (str_pos < str_length) { encode_string(str, str_pos, str_length, &working_encoding, &working_lengths, &str_pos, encoding, &best_lengths); if (str_pos < str_length) { // This is a non-match. Skip one utf-8 character. perfect = false; if (give_up_on_failure) break; int step = UNICHAR::utf8_step(str + str_pos); if (step == 0) step = 1; encoding->push_back(INVALID_UNICHAR_ID); best_lengths.push_back(step); str_pos += step; working_encoding = *encoding; working_lengths = best_lengths; } } if (lengths != NULL) *lengths = best_lengths; if (encoded_length != NULL) *encoded_length = str_pos; return perfect; } const char* UNICHARSET::id_to_unichar(UNICHAR_ID id) const { if (id == INVALID_UNICHAR_ID) { return INVALID_UNICHAR; } ASSERT_HOST(id < this->size()); return unichars[id].representation; } const char* UNICHARSET::id_to_unichar_ext(UNICHAR_ID id) const { if (id == INVALID_UNICHAR_ID) { return INVALID_UNICHAR; } ASSERT_HOST(id < this->size()); // Resolve from the kCustomLigatures table if this is a private encoding. if (get_isprivate(id)) { const char* ch = id_to_unichar(id); for (int i = 0; kCustomLigatures[i][0] != NULL; ++i) { if (!strcmp(ch, kCustomLigatures[i][1])) { return kCustomLigatures[i][0]; } } } // Otherwise return the stored representation. return unichars[id].representation; } // Return a STRING that reformats the utf8 str into the str followed // by its hex unicodes. STRING UNICHARSET::debug_utf8_str(const char* str) { STRING result = str; result += " ["; int step = 1; // Chop into unicodes and code each as hex. for (int i = 0; str[i] != '\0'; i += step) { char hex[sizeof(int) * 2 + 1]; step = UNICHAR::utf8_step(str + i); if (step == 0) { step = 1; sprintf(hex, "%x", str[i]); } else { UNICHAR ch(str + i, step); sprintf(hex, "%x", ch.first_uni()); } result += hex; result += " "; } result += "]"; return result; } // Return a STRING containing debug information on the unichar, including // the id_to_unichar, its hex unicodes and the properties. STRING UNICHARSET::debug_str(UNICHAR_ID id) const { if (id == INVALID_UNICHAR_ID) return STRING(id_to_unichar(id)); const CHAR_FRAGMENT *fragment = this->get_fragment(id); if (fragment) { return fragment->to_string(); } const char* str = id_to_unichar(id); STRING result = debug_utf8_str(str); // Append a for lower alpha, A for upper alpha, and x if alpha but neither. if (get_isalpha(id)) { if (get_islower(id)) result += "a"; else if (get_isupper(id)) result += "A"; else result += "x"; } // Append 0 if a digit. if (get_isdigit(id)) { result += "0"; } // Append p is a punctuation symbol. if (get_ispunctuation(id)) { result += "p"; } return result; } // Sets the normed_ids vector from the normed string. normed_ids is not // stored in the file, and needs to be set when the UNICHARSET is loaded. void UNICHARSET::set_normed_ids(UNICHAR_ID unichar_id) { unichars[unichar_id].properties.normed_ids.truncate(0); if (unichar_id == UNICHAR_SPACE && id_to_unichar(unichar_id)[0] == ' ') { unichars[unichar_id].properties.normed_ids.push_back(UNICHAR_SPACE); } else if (!encode_string(unichars[unichar_id].properties.normed.string(), true, &unichars[unichar_id].properties.normed_ids, NULL, NULL)) { unichars[unichar_id].properties.normed_ids.truncate(0); unichars[unichar_id].properties.normed_ids.push_back(unichar_id); } } // Returns whether the unichar id represents a unicode value in the private use // area. We use this range only internally to represent uncommon ligatures // (eg. 'ct') that do not have regular unicode values. bool UNICHARSET::get_isprivate(UNICHAR_ID unichar_id) const { UNICHAR uc(id_to_unichar(unichar_id), -1); int uni = uc.first_uni(); return (uni >= 0xE000 && uni <= 0xF8FF); } // Sets all ranges to empty, so they can be expanded to set the values. void UNICHARSET::set_ranges_empty() { for (int id = 0; id < size_used; ++id) { unichars[id].properties.SetRangesEmpty(); } } // Sets all the properties for this unicharset given a src unicharset with // everything set. The unicharsets don't have to be the same, and graphemes // are correctly accounted for. void UNICHARSET::PartialSetPropertiesFromOther(int start_index, const UNICHARSET& src) { for (int ch = start_index; ch < size_used; ++ch) { const char* utf8 = id_to_unichar(ch); UNICHAR_PROPERTIES properties; if (src.GetStrProperties(utf8, &properties)) { // Setup the script_id, other_case, and mirror properly. const char* script = src.get_script_from_script_id(properties.script_id); properties.script_id = add_script(script); const char* other_case = src.id_to_unichar(properties.other_case); if (contains_unichar(other_case)) { properties.other_case = unichar_to_id(other_case); } else { properties.other_case = ch; } const char* mirror_str = src.id_to_unichar(properties.mirror); if (contains_unichar(mirror_str)) { properties.mirror = unichar_to_id(mirror_str); } else { properties.mirror = ch; } unichars[ch].properties.CopyFrom(properties); set_normed_ids(ch); } } } // Expands the tops and bottoms and widths for this unicharset given a // src unicharset with ranges in it. The unicharsets don't have to be the // same, and graphemes are correctly accounted for. void UNICHARSET::ExpandRangesFromOther(const UNICHARSET& src) { for (int ch = 0; ch < size_used; ++ch) { const char* utf8 = id_to_unichar(ch); UNICHAR_PROPERTIES properties; if (src.GetStrProperties(utf8, &properties)) { // Expand just the ranges from properties. unichars[ch].properties.ExpandRangesFrom(properties); } } } // Makes this a copy of src. Clears this completely first, so the automatic // ids will not be present in this if not in src. Does NOT reorder the set! void UNICHARSET::CopyFrom(const UNICHARSET& src) { clear(); for (int ch = 0; ch < src.size_used; ++ch) { const UNICHAR_PROPERTIES& src_props = src.unichars[ch].properties; const char* utf8 = src.id_to_unichar(ch); unichar_insert(utf8); unichars[ch].properties.ExpandRangesFrom(src_props); } // Set properties, including mirror and other_case, WITHOUT reordering // the unicharset. PartialSetPropertiesFromOther(0, src); } // For each id in src, if it does not occur in this, add it, as in // SetPropertiesFromOther, otherwise expand the ranges, as in // ExpandRangesFromOther. void UNICHARSET::AppendOtherUnicharset(const UNICHARSET& src) { int initial_used = size_used; for (int ch = 0; ch < src.size_used; ++ch) { const UNICHAR_PROPERTIES& src_props = src.unichars[ch].properties; const char* utf8 = src.id_to_unichar(ch); if (ch >= SPECIAL_UNICHAR_CODES_COUNT && src_props.AnyRangeEmpty()) { // Only use fully valid entries. tprintf("Bad properties for index %d, char %s: " "%d,%d %d,%d %g,%g %g,%g %g,%g\n", ch, utf8, src_props.min_bottom, src_props.max_bottom, src_props.min_top, src_props.max_top, src_props.width, src_props.width_sd, src_props.bearing, src_props.bearing_sd, src_props.advance, src_props.advance_sd); continue; } int id = size_used; if (contains_unichar(utf8)) { id = unichar_to_id(utf8); // Just expand current ranges. unichars[id].properties.ExpandRangesFrom(src_props); } else { unichar_insert(utf8); unichars[id].properties.SetRangesEmpty(); } } // Set properties, including mirror and other_case, WITHOUT reordering // the unicharset. PartialSetPropertiesFromOther(initial_used, src); } // Returns true if the acceptable ranges of the tops of the characters do // not overlap, making their x-height calculations distinct. bool UNICHARSET::SizesDistinct(UNICHAR_ID id1, UNICHAR_ID id2) const { int overlap = MIN(unichars[id1].properties.max_top, unichars[id2].properties.max_top) - MAX(unichars[id1].properties.min_top, unichars[id2].properties.min_top); return overlap <= 0; } // Internal recursive version of encode_string above. // Seeks to encode the given string as a sequence of UNICHAR_IDs such that // each UNICHAR_ID uses the least possible part of the utf8 str. // It does this by depth-first tail recursion on increasing length matches // to the UNICHARSET, saving the first encountered result that encodes the // maximum total length of str. It stops on a failure to encode to make // the overall process of encoding a partially failed string more efficient. // See unicharset.h for definition of the args. void UNICHARSET::encode_string(const char* str, int str_index, int str_length, GenericVector* encoding, GenericVector* lengths, int* best_total_length, GenericVector* best_encoding, GenericVector* best_lengths) const { if (str_index > *best_total_length) { // This is the best result so far. *best_total_length = str_index; *best_encoding = *encoding; if (best_lengths != NULL) *best_lengths = *lengths; } if (str_index == str_length) return; int encoding_index = encoding->size(); // Find the length of the first matching unicharset member. int length = ids.minmatch(str + str_index); if (length == 0 || str_index + length > str_length) return; do { if (ids.contains(str + str_index, length)) { // Successful encoding so far. UNICHAR_ID id = ids.unichar_to_id(str + str_index, length); encoding->push_back(id); lengths->push_back(length); encode_string(str, str_index + length, str_length, encoding, lengths, best_total_length, best_encoding, best_lengths); if (*best_total_length == str_length) return; // Tail recursion success! // Failed with that length, truncate back and try again. encoding->truncate(encoding_index); lengths->truncate(encoding_index); } int step = UNICHAR::utf8_step(str + str_index + length); if (step == 0) step = 1; length += step; } while (length <= UNICHAR_LEN && str_index + length <= str_length); } // Gets the properties for a grapheme string, combining properties for // multiple characters in a meaningful way where possible. // Returns false if no valid match was found in the unicharset. // NOTE that script_id, mirror, and other_case refer to this unicharset on // return and will need translation if the target unicharset is different. bool UNICHARSET::GetStrProperties(const char* utf8_str, UNICHAR_PROPERTIES* props) const { props->Init(); props->SetRangesEmpty(); int total_unicodes = 0; GenericVector encoding; if (!encode_string(utf8_str, true, &encoding, NULL, NULL)) return false; // Some part was invalid. for (int i = 0; i < encoding.size(); ++i) { int id = encoding[i]; const UNICHAR_PROPERTIES& src_props = unichars[id].properties; // Logical OR all the bools. if (src_props.isalpha) props->isalpha = true; if (src_props.islower) props->islower = true; if (src_props.isupper) props->isupper = true; if (src_props.isdigit) props->isdigit = true; if (src_props.ispunctuation) props->ispunctuation = true; if (src_props.isngram) props->isngram = true; if (src_props.enabled) props->enabled = true; // Min/max the tops/bottoms. UpdateRange(src_props.min_bottom, &props->min_bottom, &props->max_bottom); UpdateRange(src_props.max_bottom, &props->min_bottom, &props->max_bottom); UpdateRange(src_props.min_top, &props->min_top, &props->max_top); UpdateRange(src_props.max_top, &props->min_top, &props->max_top); float bearing = props->advance + src_props.bearing; if (total_unicodes == 0 || bearing < props->bearing) { props->bearing = bearing; props->bearing_sd = props->advance_sd + src_props.bearing_sd; } props->advance += src_props.advance; props->advance_sd += src_props.advance_sd; // With a single width, just use the widths stored in the unicharset. props->width = src_props.width; props->width_sd = src_props.width_sd; // Use the first script id, other_case, mirror, direction. // Note that these will need translation, except direction. if (total_unicodes == 0) { props->script_id = src_props.script_id; props->other_case = src_props.other_case; props->mirror = src_props.mirror; props->direction = src_props.direction; } // The normed string for the compound character is the concatenation of // the normed versions of the individual characters. props->normed += src_props.normed; ++total_unicodes; } if (total_unicodes > 1) { // Estimate the total widths from the advance - bearing. props->width = props->advance - props->bearing; props->width_sd = props->advance_sd + props->bearing_sd; } return total_unicodes > 0; } // TODO(rays) clean-up the order of functions to match unicharset.h. unsigned int UNICHARSET::get_properties(UNICHAR_ID id) const { unsigned int properties = 0; if (this->get_isalpha(id)) properties |= ISALPHA_MASK; if (this->get_islower(id)) properties |= ISLOWER_MASK; if (this->get_isupper(id)) properties |= ISUPPER_MASK; if (this->get_isdigit(id)) properties |= ISDIGIT_MASK; if (this->get_ispunctuation(id)) properties |= ISPUNCTUATION_MASK; return properties; } char UNICHARSET::get_chartype(UNICHAR_ID id) const { if (this->get_isupper(id)) return 'A'; if (this->get_islower(id)) return 'a'; if (this->get_isalpha(id)) return 'x'; if (this->get_isdigit(id)) return '0'; if (this->get_ispunctuation(id)) return 'p'; return 0; } void UNICHARSET::unichar_insert(const char* const unichar_repr) { if (!ids.contains(unichar_repr)) { if (strlen(unichar_repr) > UNICHAR_LEN) { fprintf(stderr, "Utf8 buffer too big, size=%d for %s\n", int(strlen(unichar_repr)), unichar_repr); return; } if (size_used == size_reserved) { if (size_used == 0) reserve(8); else reserve(2 * size_used); } strcpy(unichars[size_used].representation, unichar_repr); this->set_script(size_used, null_script); // If the given unichar_repr represents a fragmented character, set // fragment property to a pointer to CHAR_FRAGMENT class instance with // information parsed from the unichar representation. Use the script // of the base unichar for the fragmented character if possible. CHAR_FRAGMENT *frag = CHAR_FRAGMENT::parse_from_string(unichar_repr); this->unichars[size_used].properties.fragment = frag; if (frag != NULL && this->contains_unichar(frag->get_unichar())) { this->unichars[size_used].properties.script_id = this->get_script(frag->get_unichar()); } this->unichars[size_used].properties.enabled = true; ids.insert(unichar_repr, size_used); ++size_used; } } bool UNICHARSET::contains_unichar(const char* const unichar_repr) const { return ids.contains(unichar_repr); } bool UNICHARSET::contains_unichar(const char* const unichar_repr, int length) const { if (length == 0) { return false; } return ids.contains(unichar_repr, length); } bool UNICHARSET::eq(UNICHAR_ID unichar_id, const char* const unichar_repr) const { return strcmp(this->id_to_unichar(unichar_id), unichar_repr) == 0; } bool UNICHARSET::save_to_string(STRING *str) const { const int kFileBufSize = 1024; char buffer[kFileBufSize + 1]; snprintf(buffer, kFileBufSize, "%d\n", this->size()); *str = buffer; for (UNICHAR_ID id = 0; id < this->size(); ++id) { int min_bottom, max_bottom, min_top, max_top; get_top_bottom(id, &min_bottom, &max_bottom, &min_top, &max_top); float width, width_sd; get_width_stats(id, &width, &width_sd); float bearing, bearing_sd; get_bearing_stats(id, &bearing, &bearing_sd); float advance, advance_sd; get_advance_stats(id, &advance, &advance_sd); unsigned int properties = this->get_properties(id); if (strcmp(this->id_to_unichar(id), " ") == 0) { snprintf(buffer, kFileBufSize, "%s %x %s %d\n", "NULL", properties, this->get_script_from_script_id(this->get_script(id)), this->get_other_case(id)); } else { snprintf(buffer, kFileBufSize, "%s %x %d,%d,%d,%d,%g,%g,%g,%g,%g,%g %s %d %d %d %s\t# %s\n", this->id_to_unichar(id), properties, min_bottom, max_bottom, min_top, max_top, width, width_sd, bearing, bearing_sd, advance, advance_sd, this->get_script_from_script_id(this->get_script(id)), this->get_other_case(id), this->get_direction(id), this->get_mirror(id), this->get_normed_unichar(id), this->debug_str(id).string()); } *str += buffer; } return true; } // TODO(rays) Replace with TFile everywhere. class InMemoryFilePointer { public: InMemoryFilePointer(const char *memory, int mem_size) : memory_(memory), fgets_ptr_(memory), mem_size_(mem_size) { } char *fgets(char *orig_dst, int size) { const char *src_end = memory_ + mem_size_; char *dst_end = orig_dst + size - 1; if (size < 1) { return fgets_ptr_ < src_end ? orig_dst : NULL; } char *dst = orig_dst; char ch = '^'; while (fgets_ptr_ < src_end && dst < dst_end && ch != '\n') { ch = *dst++ = *fgets_ptr_++; } *dst = 0; return (dst == orig_dst) ? NULL : orig_dst; } private: const char *memory_; const char *fgets_ptr_; const int mem_size_; }; bool UNICHARSET::load_from_inmemory_file( const char *memory, int mem_size, bool skip_fragments) { InMemoryFilePointer mem_fp(memory, mem_size); TessResultCallback2 *fgets_cb = NewPermanentTessCallback(&mem_fp, &InMemoryFilePointer::fgets); bool success = load_via_fgets(fgets_cb, skip_fragments); delete fgets_cb; return success; } class LocalFilePointer { public: LocalFilePointer(FILE *stream) : fp_(stream) {} char *fgets(char *dst, int size) { return ::fgets(dst, size, fp_); } private: FILE *fp_; }; bool UNICHARSET::load_from_file(FILE *file, bool skip_fragments) { LocalFilePointer lfp(file); TessResultCallback2 *fgets_cb = NewPermanentTessCallback(&lfp, &LocalFilePointer::fgets); bool success = load_via_fgets(fgets_cb, skip_fragments); delete fgets_cb; return success; } bool UNICHARSET::load_from_file(tesseract::TFile *file, bool skip_fragments) { TessResultCallback2 *fgets_cb = NewPermanentTessCallback(file, &tesseract::TFile::FGets); bool success = load_via_fgets(fgets_cb, skip_fragments); delete fgets_cb; return success; } bool UNICHARSET::load_via_fgets( TessResultCallback2 *fgets_cb, bool skip_fragments) { int unicharset_size; char buffer[256]; this->clear(); if (fgets_cb->Run(buffer, sizeof(buffer)) == NULL || sscanf(buffer, "%d", &unicharset_size) != 1) { return false; } this->reserve(unicharset_size); for (UNICHAR_ID id = 0; id < unicharset_size; ++id) { char unichar[256]; unsigned int properties; char script[64]; strcpy(script, null_script); int min_bottom = 0; int max_bottom = MAX_UINT8; int min_top = 0; int max_top = MAX_UINT8; float width = 0.0f; float width_sd = 0.0f; float bearing = 0.0f; float bearing_sd = 0.0f; float advance = 0.0f; float advance_sd = 0.0f; // TODO(eger): check that this default it ok // after enabling BiDi iterator for Arabic+Cube. int direction = UNICHARSET::U_LEFT_TO_RIGHT; UNICHAR_ID other_case = id; UNICHAR_ID mirror = id; char normed[64]; int v = -1; if (fgets_cb->Run(buffer, sizeof (buffer)) == NULL || ((v = sscanf(buffer, "%s %x %d,%d,%d,%d,%g,%g,%g,%g,%g,%g %63s %d %d %d %63s", unichar, &properties, &min_bottom, &max_bottom, &min_top, &max_top, &width, &width_sd, &bearing, &bearing_sd, &advance, &advance_sd, script, &other_case, &direction, &mirror, normed)) != 17 && (v = sscanf(buffer, "%s %x %d,%d,%d,%d,%g,%g,%g,%g,%g,%g %63s %d %d %d", unichar, &properties, &min_bottom, &max_bottom, &min_top, &max_top, &width, &width_sd, &bearing, &bearing_sd, &advance, &advance_sd, script, &other_case, &direction, &mirror)) != 16 && (v = sscanf(buffer, "%s %x %d,%d,%d,%d %63s %d %d %d", unichar, &properties, &min_bottom, &max_bottom, &min_top, &max_top, script, &other_case, &direction, &mirror)) != 10 && (v = sscanf(buffer, "%s %x %d,%d,%d,%d %63s %d", unichar, &properties, &min_bottom, &max_bottom, &min_top, &max_top, script, &other_case)) != 8 && (v = sscanf(buffer, "%s %x %63s %d", unichar, &properties, script, &other_case)) != 4 && (v = sscanf(buffer, "%s %x %63s", unichar, &properties, script)) != 3 && (v = sscanf(buffer, "%s %x", unichar, &properties)) != 2)) { return false; } // Skip fragments if needed. CHAR_FRAGMENT *frag = NULL; if (skip_fragments && (frag = CHAR_FRAGMENT::parse_from_string(unichar))) { int num_pieces = frag->get_total(); delete frag; // Skip multi-element fragments, but keep singles like UNICHAR_BROKEN in. if (num_pieces > 1) continue; } // Insert unichar into unicharset and set its properties. if (strcmp(unichar, "NULL") == 0) this->unichar_insert(" "); else this->unichar_insert(unichar); this->set_isalpha(id, properties & ISALPHA_MASK); this->set_islower(id, properties & ISLOWER_MASK); this->set_isupper(id, properties & ISUPPER_MASK); this->set_isdigit(id, properties & ISDIGIT_MASK); this->set_ispunctuation(id, properties & ISPUNCTUATION_MASK); this->set_isngram(id, false); this->set_script(id, script); this->unichars[id].properties.enabled = true; this->set_top_bottom(id, min_bottom, max_bottom, min_top, max_top); this->set_width_stats(id, width, width_sd); this->set_bearing_stats(id, bearing, bearing_sd); this->set_advance_stats(id, advance, advance_sd); this->set_direction(id, static_cast(direction)); ASSERT_HOST(other_case < unicharset_size); this->set_other_case(id, (v>3) ? other_case : id); ASSERT_HOST(mirror < unicharset_size); this->set_mirror(id, (v>8) ? mirror : id); this->set_normed(id, (v>16) ? normed : unichar); } post_load_setup(); return true; } // Sets up internal data after loading the file, based on the char // properties. Called from load_from_file, but also needs to be run // during set_unicharset_properties. void UNICHARSET::post_load_setup() { // Number of alpha chars with the case property minus those without, // in order to determine that half the alpha chars have case. int net_case_alphas = 0; int x_height_alphas = 0; int cap_height_alphas = 0; top_bottom_set_ = false; for (UNICHAR_ID id = 0; id < size_used; ++id) { int min_bottom = 0; int max_bottom = MAX_UINT8; int min_top = 0; int max_top = MAX_UINT8; get_top_bottom(id, &min_bottom, &max_bottom, &min_top, &max_top); if (min_top > 0) top_bottom_set_ = true; if (get_isalpha(id)) { if (get_islower(id) || get_isupper(id)) ++net_case_alphas; else --net_case_alphas; if (min_top < kMeanlineThreshold && max_top < kMeanlineThreshold) ++x_height_alphas; else if (min_top > kMeanlineThreshold && max_top > kMeanlineThreshold) ++cap_height_alphas; } set_normed_ids(id); } script_has_upper_lower_ = net_case_alphas > 0; script_has_xheight_ = script_has_upper_lower_ || (x_height_alphas > cap_height_alphas * kMinXHeightFraction && cap_height_alphas > x_height_alphas * kMinCapHeightFraction); null_sid_ = get_script_id_from_name(null_script); ASSERT_HOST(null_sid_ == 0); common_sid_ = get_script_id_from_name("Common"); latin_sid_ = get_script_id_from_name("Latin"); cyrillic_sid_ = get_script_id_from_name("Cyrillic"); greek_sid_ = get_script_id_from_name("Greek"); han_sid_ = get_script_id_from_name("Han"); hiragana_sid_ = get_script_id_from_name("Hiragana"); katakana_sid_ = get_script_id_from_name("Katakana"); // Compute default script. Use the highest-counting alpha script, that is // not the common script, as that still contains some "alphas". int* script_counts = new int[script_table_size_used]; memset(script_counts, 0, sizeof(*script_counts) * script_table_size_used); for (int id = 0; id < size_used; ++id) { if (get_isalpha(id)) { ++script_counts[get_script(id)]; } } default_sid_ = 0; for (int s = 1; s < script_table_size_used; ++s) { if (script_counts[s] > script_counts[default_sid_] && s != common_sid_) default_sid_ = s; } delete [] script_counts; } // Returns true if right_to_left scripts are significant in the unicharset, // but without being so sensitive that "universal" unicharsets containing // characters from many scripts, like orientation and script detection, // look like they are right_to_left. bool UNICHARSET::major_right_to_left() const { int ltr_count = 0; int rtl_count = 0; for (int id = 0; id < size_used; ++id) { int dir = get_direction(id); if (dir == UNICHARSET::U_LEFT_TO_RIGHT) ltr_count++; if (dir == UNICHARSET::U_RIGHT_TO_LEFT || dir == UNICHARSET::U_RIGHT_TO_LEFT_ARABIC || dir == UNICHARSET::U_ARABIC_NUMBER) rtl_count++; } return rtl_count > ltr_count; } // Set a whitelist and/or blacklist of characters to recognize. // An empty or NULL whitelist enables everything (minus any blacklist). // An empty or NULL blacklist disables nothing. // An empty or NULL blacklist has no effect. void UNICHARSET::set_black_and_whitelist(const char* blacklist, const char* whitelist, const char* unblacklist) { bool def_enabled = whitelist == NULL || whitelist[0] == '\0'; // Set everything to default for (int ch = 0; ch < size_used; ++ch) unichars[ch].properties.enabled = def_enabled; if (!def_enabled) { // Enable the whitelist. GenericVector encoding; encode_string(whitelist, false, &encoding, NULL, NULL); for (int i = 0; i < encoding.size(); ++i) { if (encoding[i] != INVALID_UNICHAR_ID) unichars[encoding[i]].properties.enabled = true; } } if (blacklist != NULL && blacklist[0] != '\0') { // Disable the blacklist. GenericVector encoding; encode_string(blacklist, false, &encoding, NULL, NULL); for (int i = 0; i < encoding.size(); ++i) { if (encoding[i] != INVALID_UNICHAR_ID) unichars[encoding[i]].properties.enabled = false; } } if (unblacklist != NULL && unblacklist[0] != '\0') { // Re-enable the unblacklist. GenericVector encoding; encode_string(unblacklist, false, &encoding, NULL, NULL); for (int i = 0; i < encoding.size(); ++i) { if (encoding[i] != INVALID_UNICHAR_ID) unichars[encoding[i]].properties.enabled = true; } } } // Returns true if there are any repeated unicodes in the normalized // text of any unichar-id in the unicharset. bool UNICHARSET::AnyRepeatedUnicodes() const { int start_id = 0; if (has_special_codes()) start_id = SPECIAL_UNICHAR_CODES_COUNT; for (int id = start_id; id < size_used; ++id) { // Convert to unicodes. GenericVector unicodes; if (UNICHAR::UTF8ToUnicode(get_normed_unichar(id), &unicodes) && unicodes.size() > 1) { for (int u = 1; u < unicodes.size(); ++u) { if (unicodes[u - 1] == unicodes[u]) return true; } } } return false; } int UNICHARSET::add_script(const char* script) { for (int i = 0; i < script_table_size_used; ++i) { if (strcmp(script, script_table[i]) == 0) return i; } if (script_table_size_reserved == 0) { script_table_size_reserved = 8; script_table = new char*[script_table_size_reserved]; } if (script_table_size_used + 1 >= script_table_size_reserved) { char** new_script_table = new char*[script_table_size_reserved * 2]; memcpy(new_script_table, script_table, script_table_size_reserved * sizeof(char*)); delete[] script_table; script_table = new_script_table; script_table_size_reserved = 2 * script_table_size_reserved; } script_table[script_table_size_used] = new char[strlen(script) + 1]; strcpy(script_table[script_table_size_used], script); return script_table_size_used++; } // Returns the string that represents a fragment // with the given unichar, pos and total. STRING CHAR_FRAGMENT::to_string(const char *unichar, int pos, int total, bool natural) { if (total == 1) return STRING(unichar); STRING result = ""; result += kSeparator; result += unichar; char buffer[kMaxLen]; snprintf(buffer, kMaxLen, "%c%d%c%d", kSeparator, pos, natural ? kNaturalFlag : kSeparator, total); result += buffer; return result; } CHAR_FRAGMENT *CHAR_FRAGMENT::parse_from_string(const char *string) { const char *ptr = string; int len = strlen(string); if (len < kMinLen || *ptr != kSeparator) { return NULL; // this string can not represent a fragment } ptr++; // move to the next character int step = 0; while ((ptr + step) < (string + len) && *(ptr + step) != kSeparator) { step += UNICHAR::utf8_step(ptr + step); } if (step == 0 || step > UNICHAR_LEN) { return NULL; // no character for unichar or the character is too long } char unichar[UNICHAR_LEN + 1]; strncpy(unichar, ptr, step); unichar[step] = '\0'; // null terminate unichar ptr += step; // move to the next fragment separator int pos = 0; int total = 0; bool natural = false; char *end_ptr = NULL; for (int i = 0; i < 2; i++) { if (ptr > string + len || *ptr != kSeparator) { if (i == 1 && *ptr == kNaturalFlag) natural = true; else return NULL; // Failed to parse fragment representation. } ptr++; // move to the next character i == 0 ? pos = static_cast(strtol(ptr, &end_ptr, 10)) : total = static_cast(strtol(ptr, &end_ptr, 10)); ptr = end_ptr; } if (ptr != string + len) { return NULL; // malformed fragment representation } CHAR_FRAGMENT *fragment = new CHAR_FRAGMENT(); fragment->set_all(unichar, pos, total, natural); return fragment; } int UNICHARSET::get_script_id_from_name(const char* script_name) const { for (int i = 0; i < script_table_size_used; ++i) { if (strcmp(script_name, script_table[i]) == 0) return i; } return 0; // 0 is always the null_script } tesseract-3.04.01/ccutil/unicharset.h000066400000000000000000001175271266071204500174750ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: unicharset.h // Description: Unicode character/ligature set class. // Author: Thomas Kielbus // Created: Wed Jun 28 17:05:01 PDT 2006 // // (C) Copyright 2006, 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_UNICHARSET_H__ #define TESSERACT_CCUTIL_UNICHARSET_H__ #include "errcode.h" #include "genericvector.h" #include "helpers.h" #include "serialis.h" #include "strngs.h" #include "tesscallback.h" #include "unichar.h" #include "unicharmap.h" // Enum holding special values of unichar_id. Every unicharset has these. // Warning! Keep in sync with kSpecialUnicharCodes. enum SpecialUnicharCodes { UNICHAR_SPACE, UNICHAR_JOINED, UNICHAR_BROKEN, SPECIAL_UNICHAR_CODES_COUNT }; class CHAR_FRAGMENT { public: // Minimum number of characters used for fragment representation. static const int kMinLen = 6; // Maximum number of characters used for fragment representation. static const int kMaxLen = 3 + UNICHAR_LEN + 2; // Maximum number of fragments per character. static const int kMaxChunks = 5; // Setters and Getters. inline void set_all(const char *unichar, int pos, int total, bool natural) { set_unichar(unichar); set_pos(pos); set_total(total); set_natural(natural); } inline void set_unichar(const char *uch) { strncpy(this->unichar, uch, UNICHAR_LEN); this->unichar[UNICHAR_LEN] = '\0'; } inline void set_pos(int p) { this->pos = p; } inline void set_total(int t) { this->total = t; } inline const char* get_unichar() const { return this->unichar; } inline int get_pos() const { return this->pos; } inline int get_total() const { return this->total; } // Returns the string that represents a fragment // with the given unichar, pos and total. static STRING to_string(const char *unichar, int pos, int total, bool natural); // Returns the string that represents this fragment. STRING to_string() const { return to_string(unichar, pos, total, natural); } // Checks whether a fragment has the same unichar, // position and total as the given inputs. inline bool equals(const char *other_unichar, int other_pos, int other_total) const { return (strcmp(this->unichar, other_unichar) == 0 && this->pos == other_pos && this->total == other_total); } inline bool equals(const CHAR_FRAGMENT *other) const { return this->equals(other->get_unichar(), other->get_pos(), other->get_total()); } // Checks whether a given fragment is a continuation of this fragment. // Assumes that the given fragment pointer is not NULL. inline bool is_continuation_of(const CHAR_FRAGMENT *fragment) const { return (strcmp(this->unichar, fragment->get_unichar()) == 0 && this->total == fragment->get_total() && this->pos == fragment->get_pos() + 1); } // Returns true if this fragment is a beginning fragment. inline bool is_beginning() const { return this->pos == 0; } // Returns true if this fragment is an ending fragment. inline bool is_ending() const { return this->pos == this->total-1; } // Returns true if the fragment was a separate component to begin with, // ie did not need chopping to be isolated, but may have been separated // out from a multi-outline blob. inline bool is_natural() const { return natural; } void set_natural(bool value) { natural = value; } // Parses the string to see whether it represents a character fragment // (rather than a regular character). If so, allocates memory for a new // CHAR_FRAGMENT instance and fills it in with the corresponding fragment // information. Fragments are of the form: // |m|1|2, meaning chunk 1 of 2 of character m, or // |:|1n2, meaning chunk 1 of 2 of character :, and no chopping was needed // to divide the parts, as they were already separate connected components. // // If parsing succeeded returns the pointer to the allocated CHAR_FRAGMENT // instance, otherwise (if the string does not represent a fragment or it // looks like it does, but parsing it as a fragment fails) returns NULL. // // Note: The caller is responsible for deallocating memory // associated with the returned pointer. static CHAR_FRAGMENT *parse_from_string(const char *str); private: char unichar[UNICHAR_LEN + 1]; // True if the fragment was a separate component to begin with, // ie did not need chopping to be isolated, but may have been separated // out from a multi-outline blob. bool natural; inT16 pos; // fragment position in the character inT16 total; // total number of fragments in the character }; // The UNICHARSET class is an utility class for Tesseract that holds the // set of characters that are used by the engine. Each character is identified // by a unique number, from 0 to (size - 1). class UNICHARSET { public: // Custom list of characters and their ligature forms (UTF8) // These map to unicode values in the private use area (PUC) and are supported // by only few font families (eg. Wyld, Adobe Caslon Pro). static const char* kCustomLigatures[][2]; // List of strings for the SpecialUnicharCodes. Keep in sync with the enum. static const char* kSpecialUnicharCodes[SPECIAL_UNICHAR_CODES_COUNT]; // ICU 2.0 UCharDirection enum (from third_party/icu/include/unicode/uchar.h) enum Direction { U_LEFT_TO_RIGHT = 0, U_RIGHT_TO_LEFT = 1, U_EUROPEAN_NUMBER = 2, U_EUROPEAN_NUMBER_SEPARATOR = 3, U_EUROPEAN_NUMBER_TERMINATOR = 4, U_ARABIC_NUMBER = 5, U_COMMON_NUMBER_SEPARATOR = 6, U_BLOCK_SEPARATOR = 7, U_SEGMENT_SEPARATOR = 8, U_WHITE_SPACE_NEUTRAL = 9, U_OTHER_NEUTRAL = 10, U_LEFT_TO_RIGHT_EMBEDDING = 11, U_LEFT_TO_RIGHT_OVERRIDE = 12, U_RIGHT_TO_LEFT_ARABIC = 13, U_RIGHT_TO_LEFT_EMBEDDING = 14, U_RIGHT_TO_LEFT_OVERRIDE = 15, U_POP_DIRECTIONAL_FORMAT = 16, U_DIR_NON_SPACING_MARK = 17, U_BOUNDARY_NEUTRAL = 18, U_CHAR_DIRECTION_COUNT }; // Create an empty UNICHARSET UNICHARSET(); ~UNICHARSET(); // Return the UNICHAR_ID of a given unichar representation within the // UNICHARSET. UNICHAR_ID unichar_to_id(const char* const unichar_repr) const; // Return the UNICHAR_ID of a given unichar representation within the // UNICHARSET. Only the first length characters from unichar_repr are used. UNICHAR_ID unichar_to_id(const char* const unichar_repr, int length) const; // Return the minimum number of bytes that matches a legal UNICHAR_ID, // while leaving the rest of the string encodable. Returns 0 if the // beginning of the string is not encodable. // WARNING: this function now encodes the whole string for precision. // Use encode_string in preference to repeatedly calling step. int step(const char* str) const; // Return whether the given UTF-8 string is encodable with this UNICHARSET. // If not encodable, write the first byte offset which cannot be converted // into the second (return) argument. bool encodable_string(const char *str, int *first_bad_position) const; // Encodes the given UTF-8 string with this UNICHARSET. // Any part of the string that cannot be encoded (because the utf8 can't // be broken up into pieces that are in the unicharset) then: // if give_up_on_failure, stops and returns a partial encoding, // else continues and inserts an INVALID_UNICHAR_ID in the returned encoding. // Returns true if the encoding succeeds completely, false if there is at // least one failure. // If lengths is not NULL, then it is filled with the corresponding // byte length of each encoded UNICHAR_ID. // If encoded_length is not NULL then on return it contains the length of // str that was encoded. (if give_up_on_failure the location of the first // failure, otherwise strlen(str).) bool encode_string(const char* str, bool give_up_on_failure, GenericVector* encoding, GenericVector* lengths, int* encoded_length) const; // Return the unichar representation corresponding to the given UNICHAR_ID // within the UNICHARSET. const char* id_to_unichar(UNICHAR_ID id) const; // Return the UTF8 representation corresponding to the given UNICHAR_ID after // resolving any private encodings internal to Tesseract. This method is // preferable to id_to_unichar for outputting text that will be visible to // external applications. const char* id_to_unichar_ext(UNICHAR_ID id) const; // Return a STRING that reformats the utf8 str into the str followed // by its hex unicodes. static STRING debug_utf8_str(const char* str); // Return a STRING containing debug information on the unichar, including // the id_to_unichar, its hex unicodes and the properties. STRING debug_str(UNICHAR_ID id) const; STRING debug_str(const char * unichar_repr) const { return debug_str(unichar_to_id(unichar_repr)); } // Add a unichar representation to the set. void unichar_insert(const char* const unichar_repr); // Return true if the given unichar id exists within the set. // Relies on the fact that unichar ids are contiguous in the unicharset. bool contains_unichar_id(UNICHAR_ID unichar_id) const { return unichar_id != INVALID_UNICHAR_ID && unichar_id < size_used && unichar_id >= 0; } // Return true if the given unichar representation exists within the set. bool contains_unichar(const char* const unichar_repr) const; bool contains_unichar(const char* const unichar_repr, int length) const; // Return true if the given unichar representation corresponds to the given // UNICHAR_ID within the set. bool eq(UNICHAR_ID unichar_id, const char* const unichar_repr) const; // Delete CHAR_FRAGMENTs stored in properties of unichars array. void delete_pointers_in_unichars() { for (int i = 0; i < size_used; ++i) { if (unichars[i].properties.fragment != NULL) { delete unichars[i].properties.fragment; unichars[i].properties.fragment = NULL; } } } // Clear the UNICHARSET (all the previous data is lost). void clear() { if (script_table != NULL) { for (int i = 0; i < script_table_size_used; ++i) delete[] script_table[i]; delete[] script_table; script_table = NULL; script_table_size_used = 0; } if (unichars != NULL) { delete_pointers_in_unichars(); delete[] unichars; unichars = NULL; } script_table_size_reserved = 0; size_reserved = 0; size_used = 0; ids.clear(); top_bottom_set_ = false; script_has_upper_lower_ = false; script_has_xheight_ = false; null_sid_ = 0; common_sid_ = 0; latin_sid_ = 0; cyrillic_sid_ = 0; greek_sid_ = 0; han_sid_ = 0; hiragana_sid_ = 0; katakana_sid_ = 0; } // Return the size of the set (the number of different UNICHAR it holds). int size() const { return size_used; } // Reserve enough memory space for the given number of UNICHARS void reserve(int unichars_number); // Opens the file indicated by filename and saves unicharset to that file. // Returns true if the operation is successful. bool save_to_file(const char * const filename) const { FILE* file = fopen(filename, "w+b"); if (file == NULL) return false; bool result = save_to_file(file); fclose(file); return result; } // Saves the content of the UNICHARSET to the given file. // Returns true if the operation is successful. bool save_to_file(FILE *file) const { STRING str; if (!save_to_string(&str)) return false; if (fwrite(&str[0], str.length(), 1, file) != 1) return false; return true; } bool save_to_file(tesseract::TFile *file) const { STRING str; if (!save_to_string(&str)) return false; if (file->FWrite(&str[0], str.length(), 1) != 1) return false; return true; } // Saves the content of the UNICHARSET to the given STRING. // Returns true if the operation is successful. bool save_to_string(STRING *str) const; // Load a unicharset from a unicharset file that has been loaded into // the given memory buffer. // Returns true if the operation is successful. bool load_from_inmemory_file(const char* const memory, int mem_size, bool skip_fragments); // Returns true if the operation is successful. bool load_from_inmemory_file(const char* const memory, int mem_size) { return load_from_inmemory_file(memory, mem_size, false); } // Opens the file indicated by filename and loads the UNICHARSET // from the given file. The previous data is lost. // Returns true if the operation is successful. bool load_from_file(const char* const filename, bool skip_fragments) { FILE* file = fopen(filename, "rb"); if (file == NULL) return false; bool result = load_from_file(file, skip_fragments); fclose(file); return result; } // returns true if the operation is successful. bool load_from_file(const char* const filename) { return load_from_file(filename, false); } // Loads the UNICHARSET from the given file. The previous data is lost. // Returns true if the operation is successful. bool load_from_file(FILE *file, bool skip_fragments); bool load_from_file(FILE *file) { return load_from_file(file, false); } bool load_from_file(tesseract::TFile *file, bool skip_fragments); // Sets up internal data after loading the file, based on the char // properties. Called from load_from_file, but also needs to be run // during set_unicharset_properties. void post_load_setup(); // Returns true if right_to_left scripts are significant in the unicharset, // but without being so sensitive that "universal" unicharsets containing // characters from many scripts, like orientation and script detection, // look like they are right_to_left. bool major_right_to_left() const; // Set a whitelist and/or blacklist of characters to recognize. // An empty or NULL whitelist enables everything (minus any blacklist). // An empty or NULL blacklist disables nothing. // An empty or NULL unblacklist has no effect. // The blacklist overrides the whitelist. // The unblacklist overrides the blacklist. // Each list is a string of utf8 character strings. Boundaries between // unicharset units are worked out automatically, and characters not in // the unicharset are silently ignored. void set_black_and_whitelist(const char* blacklist, const char* whitelist, const char* unblacklist); // Set the isalpha property of the given unichar to the given value. void set_isalpha(UNICHAR_ID unichar_id, bool value) { unichars[unichar_id].properties.isalpha = value; } // Set the islower property of the given unichar to the given value. void set_islower(UNICHAR_ID unichar_id, bool value) { unichars[unichar_id].properties.islower = value; } // Set the isupper property of the given unichar to the given value. void set_isupper(UNICHAR_ID unichar_id, bool value) { unichars[unichar_id].properties.isupper = value; } // Set the isdigit property of the given unichar to the given value. void set_isdigit(UNICHAR_ID unichar_id, bool value) { unichars[unichar_id].properties.isdigit = value; } // Set the ispunctuation property of the given unichar to the given value. void set_ispunctuation(UNICHAR_ID unichar_id, bool value) { unichars[unichar_id].properties.ispunctuation = value; } // Set the isngram property of the given unichar to the given value. void set_isngram(UNICHAR_ID unichar_id, bool value) { unichars[unichar_id].properties.isngram = value; } // Set the script name of the given unichar to the given value. // Value is copied and thus can be a temporary; void set_script(UNICHAR_ID unichar_id, const char* value) { unichars[unichar_id].properties.script_id = add_script(value); } // Set other_case unichar id in the properties for the given unichar id. void set_other_case(UNICHAR_ID unichar_id, UNICHAR_ID other_case) { unichars[unichar_id].properties.other_case = other_case; } // Set the direction property of the given unichar to the given value. void set_direction(UNICHAR_ID unichar_id, UNICHARSET::Direction value) { unichars[unichar_id].properties.direction = value; } // Set mirror unichar id in the properties for the given unichar id. void set_mirror(UNICHAR_ID unichar_id, UNICHAR_ID mirror) { unichars[unichar_id].properties.mirror = mirror; } // Record normalized version of unichar with the given unichar_id. void set_normed(UNICHAR_ID unichar_id, const char* normed) { unichars[unichar_id].properties.normed = normed; unichars[unichar_id].properties.normed_ids.truncate(0); } // Sets the normed_ids vector from the normed string. normed_ids is not // stored in the file, and needs to be set when the UNICHARSET is loaded. void set_normed_ids(UNICHAR_ID unichar_id); // Return the isalpha property of the given unichar. bool get_isalpha(UNICHAR_ID unichar_id) const { if (INVALID_UNICHAR_ID == unichar_id) return false; ASSERT_HOST(contains_unichar_id(unichar_id)); return unichars[unichar_id].properties.isalpha; } // Return the islower property of the given unichar. bool get_islower(UNICHAR_ID unichar_id) const { if (INVALID_UNICHAR_ID == unichar_id) return false; ASSERT_HOST(contains_unichar_id(unichar_id)); return unichars[unichar_id].properties.islower; } // Return the isupper property of the given unichar. bool get_isupper(UNICHAR_ID unichar_id) const { if (INVALID_UNICHAR_ID == unichar_id) return false; ASSERT_HOST(contains_unichar_id(unichar_id)); return unichars[unichar_id].properties.isupper; } // Return the isdigit property of the given unichar. bool get_isdigit(UNICHAR_ID unichar_id) const { if (INVALID_UNICHAR_ID == unichar_id) return false; ASSERT_HOST(contains_unichar_id(unichar_id)); return unichars[unichar_id].properties.isdigit; } // Return the ispunctuation property of the given unichar. bool get_ispunctuation(UNICHAR_ID unichar_id) const { if (INVALID_UNICHAR_ID == unichar_id) return false; ASSERT_HOST(contains_unichar_id(unichar_id)); return unichars[unichar_id].properties.ispunctuation; } // Return the isngram property of the given unichar. bool get_isngram(UNICHAR_ID unichar_id) const { if (INVALID_UNICHAR_ID == unichar_id) return false; ASSERT_HOST(contains_unichar_id(unichar_id)); return unichars[unichar_id].properties.isngram; } // Returns whether the unichar id represents a unicode value in the private // use area. bool get_isprivate(UNICHAR_ID unichar_id) const; // Returns true if the ids have useful min/max top/bottom values. bool top_bottom_useful() const { return top_bottom_set_; } // Sets all ranges to empty, so they can be expanded to set the values. void set_ranges_empty(); // Sets all the properties for this unicharset given a src_unicharset with // everything set. The unicharsets don't have to be the same, and graphemes // are correctly accounted for. void SetPropertiesFromOther(const UNICHARSET& src) { PartialSetPropertiesFromOther(0, src); } // Sets properties from Other, starting only at the given index. void PartialSetPropertiesFromOther(int start_index, const UNICHARSET& src); // Expands the tops and bottoms and widths for this unicharset given a // src_unicharset with ranges in it. The unicharsets don't have to be the // same, and graphemes are correctly accounted for. void ExpandRangesFromOther(const UNICHARSET& src); // Makes this a copy of src. Clears this completely first, so the automattic // ids will not be present in this if not in src. void CopyFrom(const UNICHARSET& src); // For each id in src, if it does not occur in this, add it, as in // SetPropertiesFromOther, otherwise expand the ranges, as in // ExpandRangesFromOther. void AppendOtherUnicharset(const UNICHARSET& src); // Returns true if the acceptable ranges of the tops of the characters do // not overlap, making their x-height calculations distinct. bool SizesDistinct(UNICHAR_ID id1, UNICHAR_ID id2) const; // Returns the min and max bottom and top of the given unichar in // baseline-normalized coordinates, ie, where the baseline is // kBlnBaselineOffset and the meanline is kBlnBaselineOffset + kBlnXHeight // (See normalis.h for the definitions). void get_top_bottom(UNICHAR_ID unichar_id, int* min_bottom, int* max_bottom, int* min_top, int* max_top) const { if (INVALID_UNICHAR_ID == unichar_id) { *min_bottom = *min_top = 0; *max_bottom = *max_top = 256; // kBlnCellHeight return; } ASSERT_HOST(contains_unichar_id(unichar_id)); *min_bottom = unichars[unichar_id].properties.min_bottom; *max_bottom = unichars[unichar_id].properties.max_bottom; *min_top = unichars[unichar_id].properties.min_top; *max_top = unichars[unichar_id].properties.max_top; } void set_top_bottom(UNICHAR_ID unichar_id, int min_bottom, int max_bottom, int min_top, int max_top) { unichars[unichar_id].properties.min_bottom = static_cast(ClipToRange(min_bottom, 0, MAX_UINT8)); unichars[unichar_id].properties.max_bottom = static_cast(ClipToRange(max_bottom, 0, MAX_UINT8)); unichars[unichar_id].properties.min_top = static_cast(ClipToRange(min_top, 0, MAX_UINT8)); unichars[unichar_id].properties.max_top = static_cast(ClipToRange(max_top, 0, MAX_UINT8)); } // Returns the width stats (as mean, sd) of the given unichar relative to the // median advance of all characters in the character set. void get_width_stats(UNICHAR_ID unichar_id, float* width, float* width_sd) const { if (INVALID_UNICHAR_ID == unichar_id) { *width = 0.0f; *width_sd = 0.0f;; return; } ASSERT_HOST(contains_unichar_id(unichar_id)); *width = unichars[unichar_id].properties.width; *width_sd = unichars[unichar_id].properties.width_sd; } void set_width_stats(UNICHAR_ID unichar_id, float width, float width_sd) { unichars[unichar_id].properties.width = width; unichars[unichar_id].properties.width_sd = width_sd; } // Returns the stats of the x-bearing (as mean, sd) of the given unichar // relative to the median advance of all characters in the character set. void get_bearing_stats(UNICHAR_ID unichar_id, float* bearing, float* bearing_sd) const { if (INVALID_UNICHAR_ID == unichar_id) { *bearing = *bearing_sd = 0.0f; return; } ASSERT_HOST(contains_unichar_id(unichar_id)); *bearing = unichars[unichar_id].properties.bearing; *bearing_sd = unichars[unichar_id].properties.bearing_sd; } void set_bearing_stats(UNICHAR_ID unichar_id, float bearing, float bearing_sd) { unichars[unichar_id].properties.bearing = bearing; unichars[unichar_id].properties.bearing_sd = bearing_sd; } // Returns the stats of the x-advance of the given unichar (as mean, sd) // relative to the median advance of all characters in the character set. void get_advance_stats(UNICHAR_ID unichar_id, float* advance, float* advance_sd) const { if (INVALID_UNICHAR_ID == unichar_id) { *advance = *advance_sd = 0; return; } ASSERT_HOST(contains_unichar_id(unichar_id)); *advance = unichars[unichar_id].properties.advance; *advance_sd = unichars[unichar_id].properties.advance_sd; } void set_advance_stats(UNICHAR_ID unichar_id, float advance, float advance_sd) { unichars[unichar_id].properties.advance = advance; unichars[unichar_id].properties.advance_sd = advance_sd; } // Returns true if the font metrics properties are empty. bool PropertiesIncomplete(UNICHAR_ID unichar_id) const { return unichars[unichar_id].properties.AnyRangeEmpty(); } // Return the script name of the given unichar. // The returned pointer will always be the same for the same script, it's // managed by unicharset and thus MUST NOT be deleted int get_script(UNICHAR_ID unichar_id) const { if (INVALID_UNICHAR_ID == unichar_id) return null_sid_; ASSERT_HOST(contains_unichar_id(unichar_id)); return unichars[unichar_id].properties.script_id; } // Return the character properties, eg. alpha/upper/lower/digit/punct, // as a bit field of unsigned int. unsigned int get_properties(UNICHAR_ID unichar_id) const; // Return the character property as a single char. If a character has // multiple attributes, the main property is defined by the following order: // upper_case : 'A' // lower_case : 'a' // alpha : 'x' // digit : '0' // punctuation: 'p' char get_chartype(UNICHAR_ID unichar_id) const; // Get other_case unichar id in the properties for the given unichar id. UNICHAR_ID get_other_case(UNICHAR_ID unichar_id) const { if (INVALID_UNICHAR_ID == unichar_id) return INVALID_UNICHAR_ID; ASSERT_HOST(contains_unichar_id(unichar_id)); return unichars[unichar_id].properties.other_case; } // Returns the direction property of the given unichar. Direction get_direction(UNICHAR_ID unichar_id) const { if (INVALID_UNICHAR_ID == unichar_id) return UNICHARSET::U_OTHER_NEUTRAL; ASSERT_HOST(contains_unichar_id(unichar_id)); return unichars[unichar_id].properties.direction; } // Get mirror unichar id in the properties for the given unichar id. UNICHAR_ID get_mirror(UNICHAR_ID unichar_id) const { if (INVALID_UNICHAR_ID == unichar_id) return INVALID_UNICHAR_ID; ASSERT_HOST(contains_unichar_id(unichar_id)); return unichars[unichar_id].properties.mirror; } // Returns UNICHAR_ID of the corresponding lower-case unichar. UNICHAR_ID to_lower(UNICHAR_ID unichar_id) const { if (INVALID_UNICHAR_ID == unichar_id) return INVALID_UNICHAR_ID; ASSERT_HOST(contains_unichar_id(unichar_id)); if (unichars[unichar_id].properties.islower) return unichar_id; return unichars[unichar_id].properties.other_case; } // Returns UNICHAR_ID of the corresponding upper-case unichar. UNICHAR_ID to_upper(UNICHAR_ID unichar_id) const { if (INVALID_UNICHAR_ID == unichar_id) return INVALID_UNICHAR_ID; ASSERT_HOST(contains_unichar_id(unichar_id)); if (unichars[unichar_id].properties.isupper) return unichar_id; return unichars[unichar_id].properties.other_case; } // Returns true if this UNICHARSET has the special codes in // SpecialUnicharCodes available. If false then there are normal unichars // at these codes and they should not be used. bool has_special_codes() const { return get_fragment(UNICHAR_BROKEN) != NULL && strcmp(id_to_unichar(UNICHAR_BROKEN), kSpecialUnicharCodes[UNICHAR_BROKEN]) == 0; } // Returns true if there are any repeated unicodes in the normalized // text of any unichar-id in the unicharset. bool AnyRepeatedUnicodes() const; // Return a pointer to the CHAR_FRAGMENT class if the given // unichar id represents a character fragment. const CHAR_FRAGMENT *get_fragment(UNICHAR_ID unichar_id) const { if (INVALID_UNICHAR_ID == unichar_id) return NULL; ASSERT_HOST(contains_unichar_id(unichar_id)); return unichars[unichar_id].properties.fragment; } // Return the isalpha property of the given unichar representation. bool get_isalpha(const char* const unichar_repr) const { return get_isalpha(unichar_to_id(unichar_repr)); } // Return the islower property of the given unichar representation. bool get_islower(const char* const unichar_repr) const { return get_islower(unichar_to_id(unichar_repr)); } // Return the isupper property of the given unichar representation. bool get_isupper(const char* const unichar_repr) const { return get_isupper(unichar_to_id(unichar_repr)); } // Return the isdigit property of the given unichar representation. bool get_isdigit(const char* const unichar_repr) const { return get_isdigit(unichar_to_id(unichar_repr)); } // Return the ispunctuation property of the given unichar representation. bool get_ispunctuation(const char* const unichar_repr) const { return get_ispunctuation(unichar_to_id(unichar_repr)); } // Return the character properties, eg. alpha/upper/lower/digit/punct, // of the given unichar representation unsigned int get_properties(const char* const unichar_repr) const { return get_properties(unichar_to_id(unichar_repr)); } char get_chartype(const char* const unichar_repr) const { return get_chartype(unichar_to_id(unichar_repr)); } // Return the script name of the given unichar representation. // The returned pointer will always be the same for the same script, it's // managed by unicharset and thus MUST NOT be deleted int get_script(const char* const unichar_repr) const { return get_script(unichar_to_id(unichar_repr)); } // Return a pointer to the CHAR_FRAGMENT class struct if the given // unichar representation represents a character fragment. const CHAR_FRAGMENT *get_fragment(const char* const unichar_repr) const { if (unichar_repr == NULL || unichar_repr[0] == '\0' || !ids.contains(unichar_repr)) { return NULL; } return get_fragment(unichar_to_id(unichar_repr)); } // Return the isalpha property of the given unichar representation. // Only the first length characters from unichar_repr are used. bool get_isalpha(const char* const unichar_repr, int length) const { return get_isalpha(unichar_to_id(unichar_repr, length)); } // Return the islower property of the given unichar representation. // Only the first length characters from unichar_repr are used. bool get_islower(const char* const unichar_repr, int length) const { return get_islower(unichar_to_id(unichar_repr, length)); } // Return the isupper property of the given unichar representation. // Only the first length characters from unichar_repr are used. bool get_isupper(const char* const unichar_repr, int length) const { return get_isupper(unichar_to_id(unichar_repr, length)); } // Return the isdigit property of the given unichar representation. // Only the first length characters from unichar_repr are used. bool get_isdigit(const char* const unichar_repr, int length) const { return get_isdigit(unichar_to_id(unichar_repr, length)); } // Return the ispunctuation property of the given unichar representation. // Only the first length characters from unichar_repr are used. bool get_ispunctuation(const char* const unichar_repr, int length) const { return get_ispunctuation(unichar_to_id(unichar_repr, length)); } // Returns normalized version of unichar with the given unichar_id. const char *get_normed_unichar(UNICHAR_ID unichar_id) const { if (unichar_id == UNICHAR_SPACE && has_special_codes()) return " "; return unichars[unichar_id].properties.normed.string(); } // Returns a vector of UNICHAR_IDs that represent the ids of the normalized // version of the given id. There may be more than one UNICHAR_ID in the // vector if unichar_id represents a ligature. const GenericVector& normed_ids(UNICHAR_ID unichar_id) const { return unichars[unichar_id].properties.normed_ids; } // Return the script name of the given unichar representation. // Only the first length characters from unichar_repr are used. // The returned pointer will always be the same for the same script, it's // managed by unicharset and thus MUST NOT be deleted int get_script(const char* const unichar_repr, int length) const { return get_script(unichar_to_id(unichar_repr, length)); } // Return the (current) number of scripts in the script table int get_script_table_size() const { return script_table_size_used; } // Return the script string from its id const char* get_script_from_script_id(int id) const { if (id >= script_table_size_used || id < 0) return null_script; return script_table[id]; } // Returns the id from the name of the script, or 0 if script is not found. // Note that this is an expensive operation since it involves iteratively // comparing strings in the script table. To avoid dependency on STL, we // won't use a hash. Instead, the calling function can use this to lookup // and save the ID for relevant scripts for fast comparisons later. int get_script_id_from_name(const char* script_name) const; // Return true if the given script is the null script bool is_null_script(const char* script) const { return script == null_script; } // Uniquify the given script. For two scripts a and b, if strcmp(a, b) == 0, // then the returned pointer will be the same. // The script parameter is copied and thus can be a temporary. int add_script(const char* script); // Return the enabled property of the given unichar. bool get_enabled(UNICHAR_ID unichar_id) const { return unichars[unichar_id].properties.enabled; } int null_sid() const { return null_sid_; } int common_sid() const { return common_sid_; } int latin_sid() const { return latin_sid_; } int cyrillic_sid() const { return cyrillic_sid_; } int greek_sid() const { return greek_sid_; } int han_sid() const { return han_sid_; } int hiragana_sid() const { return hiragana_sid_; } int katakana_sid() const { return katakana_sid_; } int default_sid() const { return default_sid_; } // Returns true if the unicharset has the concept of upper/lower case. bool script_has_upper_lower() const { return script_has_upper_lower_; } // Returns true if the unicharset has the concept of x-height. // script_has_xheight can be true even if script_has_upper_lower is not, // when the script has a sufficiently predominant top line with ascenders, // such as Devanagari and Thai. bool script_has_xheight() const { return script_has_xheight_; } private: struct UNICHAR_PROPERTIES { UNICHAR_PROPERTIES(); // Initializes all properties to sensible default values. void Init(); // Sets all ranges wide open. Initialization default in case there are // no useful values available. void SetRangesOpen(); // Sets all ranges to empty. Used before expanding with font-based data. void SetRangesEmpty(); // Returns true if any of the top/bottom/width/bearing/advance ranges/stats // is emtpy. bool AnyRangeEmpty() const; // Expands the ranges with the ranges from the src properties. void ExpandRangesFrom(const UNICHAR_PROPERTIES& src); // Copies the properties from src into this. void CopyFrom(const UNICHAR_PROPERTIES& src); bool isalpha; bool islower; bool isupper; bool isdigit; bool ispunctuation; bool isngram; bool enabled; // Possible limits of the top and bottom of the bounding box in // baseline-normalized coordinates, ie, where the baseline is // kBlnBaselineOffset and the meanline is kBlnBaselineOffset + kBlnXHeight // (See normalis.h for the definitions). uinT8 min_bottom; uinT8 max_bottom; uinT8 min_top; uinT8 max_top; // Statstics of the widths of bounding box, relative to the median advance. float width; float width_sd; // Stats of the x-bearing and advance, also relative to the median advance. float bearing; float bearing_sd; float advance; float advance_sd; int script_id; UNICHAR_ID other_case; // id of the corresponding upper/lower case unichar Direction direction; // direction of this unichar // Mirror property is useful for reverse DAWG lookup for words in // right-to-left languages (e.g. "(word)" would be in // '[open paren]' 'w' 'o' 'r' 'd' '[close paren]' in a UTF8 string. // However, what we want in our DAWG is // '[open paren]', 'd', 'r', 'o', 'w', '[close paren]' not // '[close paren]', 'd', 'r', 'o', 'w', '[open paren]'. UNICHAR_ID mirror; // A string of unichar_ids that represent the corresponding normed string. // For awkward characters like em-dash, this gives hyphen. // For ligatures, this gives the string of normal unichars. GenericVector normed_ids; STRING normed; // normalized version of this unichar // Contains meta information about the fragment if a unichar represents // a fragment of a character, otherwise should be set to NULL. // It is assumed that character fragments are added to the unicharset // after the corresponding 'base' characters. CHAR_FRAGMENT *fragment; }; struct UNICHAR_SLOT { char representation[UNICHAR_LEN + 1]; UNICHAR_PROPERTIES properties; }; // Internal recursive version of encode_string above. // str is the start of the whole string. // str_index is the current position in str. // str_length is the length of str. // encoding is a working encoding of str. // lengths is a working set of lengths of each element of encoding. // best_total_length is the longest length of str that has been successfully // encoded so far. // On return: // best_encoding contains the encoding that used the longest part of str. // best_lengths (may be null) contains the lengths of best_encoding. void encode_string(const char* str, int str_index, int str_length, GenericVector* encoding, GenericVector* lengths, int* best_total_length, GenericVector* best_encoding, GenericVector* best_lengths) const; // Gets the properties for a grapheme string, combining properties for // multiple characters in a meaningful way where possible. // Returns false if no valid match was found in the unicharset. // NOTE that script_id, mirror, and other_case refer to this unicharset on // return and will need redirecting if the target unicharset is different. bool GetStrProperties(const char* utf8_str, UNICHAR_PROPERTIES* props) const; // Load ourselves from a "file" where our only interface to the file is // an implementation of fgets(). This is the parsing primitive accessed by // the public routines load_from_file() and load_from_inmemory_file(). bool load_via_fgets(TessResultCallback2 *fgets_cb, bool skip_fragments); UNICHAR_SLOT* unichars; UNICHARMAP ids; int size_used; int size_reserved; char** script_table; int script_table_size_used; int script_table_size_reserved; const char* null_script; // True if the unichars have their tops/bottoms set. bool top_bottom_set_; // True if the unicharset has significant upper/lower case chars. bool script_has_upper_lower_; // True if the unicharset has a significant mean-line with significant // ascenders above that. bool script_has_xheight_; // A few convenient script name-to-id mapping without using hash. // These are initialized when unicharset file is loaded. Anything // missing from this list can be looked up using get_script_id_from_name. int null_sid_; int common_sid_; int latin_sid_; int cyrillic_sid_; int greek_sid_; int han_sid_; int hiragana_sid_; int katakana_sid_; // The most frequently occurring script in the charset. int default_sid_; }; #endif // TESSERACT_CCUTIL_UNICHARSET_H__ tesseract-3.04.01/ccutil/unicity_table.h000066400000000000000000000141301266071204500201450ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: UnicityTable.h // Description: a class to uniquify objects, manipulating them using integers // ids. // Author: Samuel Charron // // (C) Copyright 2006, 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_UNICITY_TABLE_H_ #define TESSERACT_CCUTIL_UNICITY_TABLE_H_ #include "tesscallback.h" #include "errcode.h" #include "genericvector.h" // A class to uniquify objects, manipulating them using integers ids. // T requirements: // operator= to add an element // default-constructible: allocating the internal table will call the default // constructor. template class UnicityTable { public: UnicityTable(); /// Clear the structures and deallocate internal structures. ~UnicityTable(); /// Reserve some memory. If there is size or more elements, the table will /// then allocate size * 2 elements. void reserve(int size); /// Return the size used. int size() const; /// Return the object from an id. const T &get(int id) const; // Return the pointer to an object with the given id. T *get_mutable(int id); /// Return the id of the T object. /// This method NEEDS a compare_callback to be passed to /// set_compare_callback. int get_id(T object) const; /// Return true if T is in the table bool contains(T object) const; /// Return true if the id is valid T contains_id(int id) const; /// Add an element in the table int push_back(T object); /// Add a callback to be called to delete the elements when the table took /// their ownership. void set_clear_callback(TessCallback1* cb); /// Add a callback to be called to compare the elements when needed (contains, /// get_id, ...) void set_compare_callback(TessResultCallback2* cb); /// Clear the table, calling the callback function if any. /// 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. void clear(); /// This method clear the current object, then, does a shallow copy of /// its argument, and finally invalidate its argument. void move(UnicityTable* from); /// Read/Write the table to a file. This does _NOT_ read/write the callbacks. /// The Callback given must be permanent since they will be called more than /// once. The given callback will be deleted at the end. /// Returns false on read/write error. bool write(FILE* f, TessResultCallback2* cb) const; /// swap is used to switch the endianness. bool read(FILE* f, TessResultCallback3* cb, bool swap); private: GenericVector table_; // Mutable because Run method is not const mutable TessResultCallback2* compare_cb_; }; template class UnicityTableEqEq : public UnicityTable { public: UnicityTableEqEq() { UnicityTable::set_compare_callback( NewPermanentTessCallback(tesseract::cmp_eq)); } }; template UnicityTable::UnicityTable() : compare_cb_(0) { } template UnicityTable::~UnicityTable() { clear(); } template int UnicityTable::size() const{ return table_.size(); } // Reserve some memory. If there is size or more elements, the table will // then allocate size * 2 elements. template void UnicityTable::reserve(int size) { table_.reserve(size); } // Return the object from an id. template const T &UnicityTable::get(int id) const { return table_.get(id); } // Returns the pointer to the object with the given id. template T *UnicityTable::get_mutable(int id) { return &(table_.get(id)); } // Return true if the id is valid template T UnicityTable::contains_id(int id) const { return table_.contains_index(id); } // Return the id of the T object. template int UnicityTable::get_id(T object) const { return table_.get_index(object); } // Return true if T is in the table template bool UnicityTable::contains(T object) const { return get_id(object) != -1; } // Add an element in the table template int UnicityTable::push_back(T object) { int idx = get_id(object); if (idx == -1) { idx = table_.push_back(object); } return idx; } // Add a callback to be called to delete the elements when the table took // their ownership. template void UnicityTable::set_clear_callback(TessCallback1* cb) { table_.set_clear_callback(cb); } // Add a callback to be called to delete the elements when the table took // their ownership. template void UnicityTable::set_compare_callback(TessResultCallback2* cb) { table_.set_compare_callback(cb); compare_cb_ = cb; } // Clear the table, calling the callback function if any. template void UnicityTable::clear() { table_.clear(); } template bool UnicityTable::write( FILE* f, TessResultCallback2* cb) const { return table_.write(f, cb); } template bool UnicityTable::read( FILE* f, TessResultCallback3* cb, bool swap) { return table_.read(f, cb, swap); } // This method clear the current object, then, does a shallow copy of // its argument, and finally invalidate its argument. template void UnicityTable::move(UnicityTable* from) { table_.move(&from->table_); } #endif // TESSERACT_CCUTIL_UNICITY_TABLE_H_ tesseract-3.04.01/ccutil/unicodes.cpp000066400000000000000000000040071266071204500174600ustar00rootroot00000000000000/********************************************************************** * File: unicodes.h * Description: Unicode related machinery * Author: David Eger * Created: Wed Jun 15 16:37:50 PST 2011 * * (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. * **********************************************************************/ #include "unicodes.h" #include "host.h" // for NULL namespace tesseract { const char *kUTF8LineSeparator = "\u2028"; // "\xe2\x80\xa8"; const char *kUTF8ParagraphSeparator = "\u2029"; // "\xe2\x80\xa9"; const char *kLRM = "\u200E"; // Left-to-Right Mark const char *kRLM = "\u200F"; // Right-to-Left Mark const char *kRLE = "\u202A"; // Right-to-Left Embedding const char *kPDF = "\u202C"; // Pop Directional Formatting const char *kHyphenLikeUTF8[] = { "-", // ASCII hyphen-minus "\u05BE", // word hyphen in hybrew "\u2010", // hyphen "\u2011", // non-breaking hyphen "\u2012", // a hyphen the same width as digits "\u2013", // en dash "\u2014", // em dash "\u2015", // horizontal bar "\u2212", // arithmetic minus sign "\uFE58", // small em dash "\uFE63", // small hyphen-minus "\uFF0D", // fullwidth hyphen-minus NULL, // end of our list }; const char *kApostropheLikeUTF8[] = { "'", // ASCII apostrophe "`", // ASCII backtick "\u2018", // opening single quote "\u2019", // closing single quote "\u2032", // mathematical prime mark NULL, // end of our list. }; } // namespace tesseract-3.04.01/ccutil/unicodes.h000066400000000000000000000030161266071204500171240ustar00rootroot00000000000000/********************************************************************** * File: unicodes.h * Description: Unicode related machinery * Author: David Eger * Created: Wed Jun 15 16:37:50 PST 2011 * * (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_CCUTIL_UNICODES_H__ #define TESSERACT_CCUTIL_UNICODES_H__ namespace tesseract { extern const char *kUTF8LineSeparator; extern const char *kUTF8ParagraphSeparator; extern const char *kLRM; //< Left-to-Right Mark extern const char *kRLM; //< Right-to-Left Mark extern const char *kRLE; //< Right-to-Left Embedding extern const char *kPDF; //< Pop Directional Formatting /// The following are confusable internal word punctuation symbols /// which we normalize to the first variant when matching in dawgs. extern const char *kHyphenLikeUTF8[]; extern const char *kApostropheLikeUTF8[]; } // namespace #endif // TESSERACT_CCUTIL_UNICODES_H__ tesseract-3.04.01/ccutil/universalambigs.cpp000066400000000000000000054052431266071204500210560ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // 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', '\157', '\040', '\061', '\012', '\130', '\167', '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\114', '\161', '\146', '\040', '\161', '\165', '\040', '\061', '\012', '\150', '\143', '\120', '\040', '\164', '\150', '\040', '\061', '\012', '\160', '\146', '\102', '\040', '\160', '\162', '\040', '\061', '\012', '\166', '\123', '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\170', '\112', '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\155', '\122', '\146', '\040', '\155', '\145', '\040', '\061', '\012', '\150', '\161', '\127', '\040', '\164', '\150', '\040', '\061', '\012', '\156', '\126', '\142', '\040', '\141', '\156', '\040', '\061', '\012', '\143', '\105', '\165', '\040', '\143', '\150', '\040', '\061', '\012', '\156', '\146', '\116', '\040', '\141', '\156', '\040', '\061', '\012', '\156', '\126', '\152', '\040', '\141', '\156', '\040', '\061', '\012', '\122', '\167', '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\156', '\155', '\107', '\040', '\141', '\156', '\040', '\061', '\012', '\157', '\104', '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\153', '\120', '\142', '\040', '\153', '\141', '\040', '\061', '\012', '\147', '\161', '\127', '\040', '\161', '\165', '\040', '\061', '\012', '\121', '\150', '\146', '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\132', '\154', '\040', '\161', '\165', '\040', '\061', '\012', '\172', '\110', '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\151', '\130', '\154', '\040', '\151', '\156', '\040', '\061', '\012', }; extern const int ksizeofUniversalAmbigsFile = sizeof(kUniversalAmbigsFile); } // namespace tesseract tesseract-3.04.01/ccutil/universalambigs.h000066400000000000000000000020571266071204500205120ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // 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 tesseract-3.04.01/classify/000077500000000000000000000000001266071204500154745ustar00rootroot00000000000000tesseract-3.04.01/classify/Makefile.am000066400000000000000000000034071266071204500175340ustar00rootroot00000000000000AM_CPPFLAGS += \ -I$(top_srcdir)/cutil -I$(top_srcdir)/ccutil \ -I$(top_srcdir)/ccstruct -I$(top_srcdir)/dict \ -I$(top_srcdir)/viewer if VISIBILITY AM_CPPFLAGS += -DTESS_EXPORTS \ -fvisibility=hidden -fvisibility-inlines-hidden endif noinst_HEADERS = \ adaptive.h blobclass.h \ classify.h cluster.h clusttool.h cutoffs.h \ errorcounter.h \ featdefs.h float2int.h fpoint.h \ intfeaturedist.h intfeaturemap.h intfeaturespace.h \ intfx.h intmatcher.h intproto.h kdtree.h \ mastertrainer.h mf.h mfdefs.h mfoutline.h mfx.h \ normfeat.h normmatch.h \ ocrfeatures.h outfeat.h picofeat.h protos.h \ sampleiterator.h shapeclassifier.h shapetable.h \ tessclassifier.h trainingsample.h trainingsampleset.h if !USING_MULTIPLELIBS noinst_LTLIBRARIES = libtesseract_classify.la else lib_LTLIBRARIES = libtesseract_classify.la libtesseract_classify_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) libtesseract_classify_la_LIBADD = \ ../ccutil/libtesseract_ccutil.la \ ../cutil/libtesseract_cutil.la \ ../ccstruct/libtesseract_ccstruct.la \ ../dict/libtesseract_dict.la \ ../viewer/libtesseract_viewer.la endif libtesseract_classify_la_SOURCES = \ adaptive.cpp adaptmatch.cpp blobclass.cpp \ classify.cpp cluster.cpp clusttool.cpp cutoffs.cpp \ errorcounter.cpp \ featdefs.cpp float2int.cpp fpoint.cpp \ intfeaturedist.cpp intfeaturemap.cpp intfeaturespace.cpp \ intfx.cpp intmatcher.cpp intproto.cpp kdtree.cpp \ mastertrainer.cpp mf.cpp mfdefs.cpp mfoutline.cpp mfx.cpp \ normfeat.cpp normmatch.cpp \ ocrfeatures.cpp outfeat.cpp picofeat.cpp protos.cpp \ sampleiterator.cpp shapeclassifier.cpp shapetable.cpp \ tessclassifier.cpp trainingsample.cpp trainingsampleset.cpp tesseract-3.04.01/classify/Makefile.in000066400000000000000000000641501266071204500175470ustar00rootroot00000000000000# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @VISIBILITY_TRUE@am__append_1 = -DTESS_EXPORTS \ @VISIBILITY_TRUE@ -fvisibility=hidden -fvisibility-inlines-hidden subdir = classify DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/config/depcomp $(noinst_HEADERS) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config_auto.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) @USING_MULTIPLELIBS_TRUE@libtesseract_classify_la_DEPENDENCIES = \ @USING_MULTIPLELIBS_TRUE@ ../ccutil/libtesseract_ccutil.la \ @USING_MULTIPLELIBS_TRUE@ ../cutil/libtesseract_cutil.la \ @USING_MULTIPLELIBS_TRUE@ ../ccstruct/libtesseract_ccstruct.la \ @USING_MULTIPLELIBS_TRUE@ ../dict/libtesseract_dict.la \ @USING_MULTIPLELIBS_TRUE@ ../viewer/libtesseract_viewer.la am_libtesseract_classify_la_OBJECTS = adaptive.lo adaptmatch.lo \ blobclass.lo classify.lo cluster.lo clusttool.lo cutoffs.lo \ errorcounter.lo featdefs.lo float2int.lo fpoint.lo \ intfeaturedist.lo intfeaturemap.lo intfeaturespace.lo intfx.lo \ intmatcher.lo intproto.lo kdtree.lo mastertrainer.lo mf.lo \ mfdefs.lo mfoutline.lo mfx.lo normfeat.lo normmatch.lo \ ocrfeatures.lo outfeat.lo picofeat.lo protos.lo \ sampleiterator.lo shapeclassifier.lo shapetable.lo \ tessclassifier.lo trainingsample.lo trainingsampleset.lo libtesseract_classify_la_OBJECTS = \ $(am_libtesseract_classify_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libtesseract_classify_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ $(AM_CXXFLAGS) $(CXXFLAGS) $(libtesseract_classify_la_LDFLAGS) \ $(LDFLAGS) -o $@ @USING_MULTIPLELIBS_FALSE@am_libtesseract_classify_la_rpath = @USING_MULTIPLELIBS_TRUE@am_libtesseract_classify_la_rpath = -rpath \ @USING_MULTIPLELIBS_TRUE@ $(libdir) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__depfiles_maybe = depfiles am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(libtesseract_classify_la_SOURCES) DIST_SOURCES = $(libtesseract_classify_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_CPPFLAGS = @AM_CPPFLAGS@ -I$(top_srcdir)/cutil \ -I$(top_srcdir)/ccutil -I$(top_srcdir)/ccstruct \ -I$(top_srcdir)/dict -I$(top_srcdir)/viewer $(am__append_1) AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AM_LDFLAGS = @AM_LDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FRAMEWORK_OPENCL = @FRAMEWORK_OPENCL@ GENERIC_API_VERSION = @GENERIC_API_VERSION@ GENERIC_LIBRARY_NAME = @GENERIC_LIBRARY_NAME@ GENERIC_LIBRARY_VERSION = @GENERIC_LIBRARY_VERSION@ GENERIC_MAJOR_VERSION = @GENERIC_MAJOR_VERSION@ GENERIC_RELEASE = @GENERIC_RELEASE@ GENERIC_VERSION = @GENERIC_VERSION@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBLEPT_HEADERSDIR = @LIBLEPT_HEADERSDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCL_CPPFLAGS = @OPENCL_CPPFLAGS@ OPENCL_LDFLAGS = @OPENCL_LDFLAGS@ OPENMP_CXXFLAGS = @OPENMP_CXXFLAGS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_DATE = @PACKAGE_DATE@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_YEAR = @PACKAGE_YEAR@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_HEADERS = \ adaptive.h blobclass.h \ classify.h cluster.h clusttool.h cutoffs.h \ errorcounter.h \ featdefs.h float2int.h fpoint.h \ intfeaturedist.h intfeaturemap.h intfeaturespace.h \ intfx.h intmatcher.h intproto.h kdtree.h \ mastertrainer.h mf.h mfdefs.h mfoutline.h mfx.h \ normfeat.h normmatch.h \ ocrfeatures.h outfeat.h picofeat.h protos.h \ sampleiterator.h shapeclassifier.h shapetable.h \ tessclassifier.h trainingsample.h trainingsampleset.h @USING_MULTIPLELIBS_FALSE@noinst_LTLIBRARIES = libtesseract_classify.la @USING_MULTIPLELIBS_TRUE@lib_LTLIBRARIES = libtesseract_classify.la @USING_MULTIPLELIBS_TRUE@libtesseract_classify_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) @USING_MULTIPLELIBS_TRUE@libtesseract_classify_la_LIBADD = \ @USING_MULTIPLELIBS_TRUE@ ../ccutil/libtesseract_ccutil.la \ @USING_MULTIPLELIBS_TRUE@ ../cutil/libtesseract_cutil.la \ @USING_MULTIPLELIBS_TRUE@ ../ccstruct/libtesseract_ccstruct.la \ @USING_MULTIPLELIBS_TRUE@ ../dict/libtesseract_dict.la \ @USING_MULTIPLELIBS_TRUE@ ../viewer/libtesseract_viewer.la libtesseract_classify_la_SOURCES = \ adaptive.cpp adaptmatch.cpp blobclass.cpp \ classify.cpp cluster.cpp clusttool.cpp cutoffs.cpp \ errorcounter.cpp \ featdefs.cpp float2int.cpp fpoint.cpp \ intfeaturedist.cpp intfeaturemap.cpp intfeaturespace.cpp \ intfx.cpp intmatcher.cpp intproto.cpp kdtree.cpp \ mastertrainer.cpp mf.cpp mfdefs.cpp mfoutline.cpp mfx.cpp \ normfeat.cpp normmatch.cpp \ ocrfeatures.cpp outfeat.cpp picofeat.cpp protos.cpp \ sampleiterator.cpp shapeclassifier.cpp shapetable.cpp \ tessclassifier.cpp trainingsample.cpp trainingsampleset.cpp all: all-am .SUFFIXES: .SUFFIXES: .cpp .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign classify/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign classify/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libtesseract_classify.la: $(libtesseract_classify_la_OBJECTS) $(libtesseract_classify_la_DEPENDENCIES) $(EXTRA_libtesseract_classify_la_DEPENDENCIES) $(AM_V_CXXLD)$(libtesseract_classify_la_LINK) $(am_libtesseract_classify_la_rpath) $(libtesseract_classify_la_OBJECTS) $(libtesseract_classify_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adaptive.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adaptmatch.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blobclass.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/classify.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cluster.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clusttool.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cutoffs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errorcounter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/featdefs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/float2int.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fpoint.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/intfeaturedist.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/intfeaturemap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/intfeaturespace.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/intfx.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/intmatcher.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/intproto.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kdtree.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mastertrainer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mfdefs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mfoutline.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mfx.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/normfeat.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/normmatch.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocrfeatures.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/outfeat.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/picofeat.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sampleiterator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shapeclassifier.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shapetable.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tessclassifier.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trainingsample.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trainingsampleset.Plo@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ clean-noinstLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-libLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-libLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libLTLIBRARIES clean-libtool clean-noinstLTLIBRARIES \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-libLTLIBRARIES \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-libLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: tesseract-3.04.01/classify/adaptive.cpp000066400000000000000000000444711266071204500200070ustar00rootroot00000000000000/****************************************************************************** ** Filename: adaptive.c ** Purpose: Adaptive matcher. ** Author: Dan Johnson ** History: Fri Mar 8 10:00:21 1991, 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 "adaptive.h" #include "emalloc.h" #include "freelist.h" #include "globals.h" #include "classify.h" #ifdef __UNIX__ #include #endif #include /*---------------------------------------------------------------------------- Public Code ----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /** * This routine adds a new adapted class to an existing * set of adapted templates. * * @param Templates set of templates to add new class to * @param Class new class to add to templates * @param ClassId class id to associate with new class * * @note Globals: none * @note Exceptions: none * @note History: Thu Mar 14 13:06:09 1991, DSJ, Created. */ void AddAdaptedClass(ADAPT_TEMPLATES Templates, ADAPT_CLASS Class, CLASS_ID ClassId) { INT_CLASS IntClass; assert (Templates != NULL); assert (Class != NULL); assert (LegalClassId (ClassId)); assert (UnusedClassIdIn (Templates->Templates, ClassId)); assert (Class->NumPermConfigs == 0); IntClass = NewIntClass (1, 1); AddIntClass (Templates->Templates, ClassId, IntClass); assert (Templates->Class[ClassId] == NULL); Templates->Class[ClassId] = Class; } /* AddAdaptedClass */ /*---------------------------------------------------------------------------*/ /** * This routine frees all memory consumed by a temporary * configuration. * * @param Config config to be freed * * @note Globals: none * @note Exceptions: none * @note History: Thu Mar 14 13:34:23 1991, DSJ, Created. */ void FreeTempConfig(TEMP_CONFIG Config) { assert (Config != NULL); destroy_nodes (Config->ContextsSeen, memfree); FreeBitVector (Config->Protos); free_struct (Config, sizeof (TEMP_CONFIG_STRUCT), "TEMP_CONFIG_STRUCT"); } /* FreeTempConfig */ /*---------------------------------------------------------------------------*/ void FreeTempProto(void *arg) { PROTO proto = (PROTO) arg; free_struct (proto, sizeof (TEMP_PROTO_STRUCT), "TEMP_PROTO_STRUCT"); } void FreePermConfig(PERM_CONFIG Config) { assert(Config != NULL); delete [] Config->Ambigs; free_struct(Config, sizeof(PERM_CONFIG_STRUCT), "PERM_CONFIG_STRUCT"); } /*---------------------------------------------------------------------------*/ /** * This operation allocates and initializes a new adapted * class data structure and returns a ptr to it. * * @return Ptr to new class data structure. * * @note Globals: none * @note Exceptions: none * @note History: Thu Mar 14 12:58:13 1991, DSJ, Created. */ ADAPT_CLASS NewAdaptedClass() { ADAPT_CLASS Class; int i; Class = (ADAPT_CLASS) Emalloc (sizeof (ADAPT_CLASS_STRUCT)); Class->NumPermConfigs = 0; Class->MaxNumTimesSeen = 0; Class->TempProtos = NIL_LIST; Class->PermProtos = NewBitVector (MAX_NUM_PROTOS); Class->PermConfigs = NewBitVector (MAX_NUM_CONFIGS); zero_all_bits (Class->PermProtos, WordsInVectorOfSize (MAX_NUM_PROTOS)); zero_all_bits (Class->PermConfigs, WordsInVectorOfSize (MAX_NUM_CONFIGS)); for (i = 0; i < MAX_NUM_CONFIGS; i++) TempConfigFor (Class, i) = NULL; return (Class); } /* NewAdaptedClass */ /*-------------------------------------------------------------------------*/ void free_adapted_class(ADAPT_CLASS adapt_class) { int i; for (i = 0; i < MAX_NUM_CONFIGS; i++) { if (ConfigIsPermanent (adapt_class, i) && PermConfigFor (adapt_class, i) != NULL) FreePermConfig (PermConfigFor (adapt_class, i)); else if (!ConfigIsPermanent (adapt_class, i) && TempConfigFor (adapt_class, i) != NULL) FreeTempConfig (TempConfigFor (adapt_class, i)); } FreeBitVector (adapt_class->PermProtos); FreeBitVector (adapt_class->PermConfigs); destroy_nodes (adapt_class->TempProtos, FreeTempProto); Efree(adapt_class); } /*---------------------------------------------------------------------------*/ namespace tesseract { /** * Allocates memory for adapted tempates. * each char in unicharset to the newly created templates * * @param InitFromUnicharset if true, add an empty class for * @return Ptr to new adapted templates. * * @note Globals: none * @note Exceptions: none * @note History: Fri Mar 8 10:15:28 1991, DSJ, Created. */ ADAPT_TEMPLATES Classify::NewAdaptedTemplates(bool InitFromUnicharset) { ADAPT_TEMPLATES Templates; int i; Templates = (ADAPT_TEMPLATES) Emalloc (sizeof (ADAPT_TEMPLATES_STRUCT)); Templates->Templates = NewIntTemplates (); Templates->NumPermClasses = 0; Templates->NumNonEmptyClasses = 0; /* Insert an empty class for each unichar id in unicharset */ for (i = 0; i < MAX_NUM_CLASSES; i++) { Templates->Class[i] = NULL; if (InitFromUnicharset && i < unicharset.size()) { AddAdaptedClass(Templates, NewAdaptedClass(), i); } } return (Templates); } /* NewAdaptedTemplates */ // Returns FontinfoId of the given config of the given adapted class. int Classify::GetFontinfoId(ADAPT_CLASS Class, uinT8 ConfigId) { return (ConfigIsPermanent(Class, ConfigId) ? PermConfigFor(Class, ConfigId)->FontinfoId : TempConfigFor(Class, ConfigId)->FontinfoId); } } // namespace tesseract /*----------------------------------------------------------------------------*/ void free_adapted_templates(ADAPT_TEMPLATES templates) { if (templates != NULL) { int i; for (i = 0; i < (templates->Templates)->NumClasses; i++) free_adapted_class (templates->Class[i]); free_int_templates (templates->Templates); Efree(templates); } } /*---------------------------------------------------------------------------*/ /** * This routine allocates and returns a new temporary config. * * @param MaxProtoId max id of any proto in new config * @param FontinfoId font information from pre-trained templates * @return Ptr to new temp config. * * @note Globals: none * @note Exceptions: none * @note History: Thu Mar 14 13:28:21 1991, DSJ, Created. */ TEMP_CONFIG NewTempConfig(int MaxProtoId, int FontinfoId) { TEMP_CONFIG Config; int NumProtos = MaxProtoId + 1; Config = (TEMP_CONFIG) alloc_struct (sizeof (TEMP_CONFIG_STRUCT), "TEMP_CONFIG_STRUCT"); Config->Protos = NewBitVector (NumProtos); Config->NumTimesSeen = 1; Config->MaxProtoId = MaxProtoId; Config->ProtoVectorSize = WordsInVectorOfSize (NumProtos); Config->ContextsSeen = NIL_LIST; zero_all_bits (Config->Protos, Config->ProtoVectorSize); Config->FontinfoId = FontinfoId; return (Config); } /* NewTempConfig */ /*---------------------------------------------------------------------------*/ /** * This routine allocates and returns a new temporary proto. * * @return Ptr to new temporary proto. * * @note Globals: none * @note Exceptions: none * @note History: Thu Mar 14 13:31:31 1991, DSJ, Created. */ TEMP_PROTO NewTempProto() { return ((TEMP_PROTO) alloc_struct (sizeof (TEMP_PROTO_STRUCT), "TEMP_PROTO_STRUCT")); } /* NewTempProto */ /*---------------------------------------------------------------------------*/ namespace tesseract { /** * This routine prints a summary of the adapted templates * in Templates to File. * * @param File open text file to print Templates to * @param Templates adapted templates to print to File * * @note Globals: none * @note Exceptions: none * @note History: Wed Mar 20 13:35:29 1991, DSJ, Created. */ void Classify::PrintAdaptedTemplates(FILE *File, ADAPT_TEMPLATES Templates) { int i; INT_CLASS IClass; ADAPT_CLASS AClass; fprintf (File, "\n\nSUMMARY OF ADAPTED TEMPLATES:\n\n"); fprintf (File, "Num classes = %d; Num permanent classes = %d\n\n", Templates->NumNonEmptyClasses, Templates->NumPermClasses); fprintf (File, " Id NC NPC NP NPP\n"); fprintf (File, "------------------------\n"); for (i = 0; i < (Templates->Templates)->NumClasses; i++) { IClass = Templates->Templates->Class[i]; AClass = Templates->Class[i]; if (!IsEmptyAdaptedClass (AClass)) { fprintf (File, "%5d %s %3d %3d %3d %3d\n", i, unicharset.id_to_unichar(i), IClass->NumConfigs, AClass->NumPermConfigs, IClass->NumProtos, IClass->NumProtos - count (AClass->TempProtos)); } } fprintf (File, "\n"); } /* PrintAdaptedTemplates */ } // namespace tesseract /*---------------------------------------------------------------------------*/ /** * Read an adapted class description from File and return * a ptr to the adapted class. * * @param File open file to read adapted class from * @return Ptr to new adapted class. * * @note Globals: none * @note Exceptions: none * @note History: Tue Mar 19 14:11:01 1991, DSJ, Created. */ ADAPT_CLASS ReadAdaptedClass(FILE *File) { int NumTempProtos; int NumConfigs; int i; ADAPT_CLASS Class; TEMP_PROTO TempProto; /* first read high level adapted class structure */ Class = (ADAPT_CLASS) Emalloc (sizeof (ADAPT_CLASS_STRUCT)); fread ((char *) Class, sizeof (ADAPT_CLASS_STRUCT), 1, File); /* then read in the definitions of the permanent protos and configs */ Class->PermProtos = NewBitVector (MAX_NUM_PROTOS); Class->PermConfigs = NewBitVector (MAX_NUM_CONFIGS); fread ((char *) Class->PermProtos, sizeof (uinT32), WordsInVectorOfSize (MAX_NUM_PROTOS), File); fread ((char *) Class->PermConfigs, sizeof (uinT32), WordsInVectorOfSize (MAX_NUM_CONFIGS), File); /* then read in the list of temporary protos */ fread ((char *) &NumTempProtos, sizeof (int), 1, File); Class->TempProtos = NIL_LIST; for (i = 0; i < NumTempProtos; i++) { TempProto = (TEMP_PROTO) alloc_struct (sizeof (TEMP_PROTO_STRUCT), "TEMP_PROTO_STRUCT"); fread ((char *) TempProto, sizeof (TEMP_PROTO_STRUCT), 1, File); Class->TempProtos = push_last (Class->TempProtos, TempProto); } /* then read in the adapted configs */ fread ((char *) &NumConfigs, sizeof (int), 1, File); for (i = 0; i < NumConfigs; i++) if (test_bit (Class->PermConfigs, i)) Class->Config[i].Perm = ReadPermConfig (File); else Class->Config[i].Temp = ReadTempConfig (File); return (Class); } /* ReadAdaptedClass */ /*---------------------------------------------------------------------------*/ namespace tesseract { /** * Read a set of adapted templates from File and return * a ptr to the templates. * * @param File open text file to read adapted templates from * @return Ptr to adapted templates read from File. * * @note Globals: none * @note Exceptions: none * @note History: Mon Mar 18 15:18:10 1991, DSJ, Created. */ ADAPT_TEMPLATES Classify::ReadAdaptedTemplates(FILE *File) { int i; ADAPT_TEMPLATES Templates; /* first read the high level adaptive template struct */ Templates = (ADAPT_TEMPLATES) Emalloc (sizeof (ADAPT_TEMPLATES_STRUCT)); fread ((char *) Templates, sizeof (ADAPT_TEMPLATES_STRUCT), 1, File); /* then read in the basic integer templates */ Templates->Templates = ReadIntTemplates (File); /* then read in the adaptive info for each class */ for (i = 0; i < (Templates->Templates)->NumClasses; i++) { Templates->Class[i] = ReadAdaptedClass (File); } return (Templates); } /* ReadAdaptedTemplates */ } // namespace tesseract /*---------------------------------------------------------------------------*/ /** * Read a permanent configuration description from File * and return a ptr to it. * * @param File open file to read permanent config from * @return Ptr to new permanent configuration description. * * @note Globals: none * @note Exceptions: none * @note History: Tue Mar 19 14:25:26 1991, DSJ, Created. */ PERM_CONFIG ReadPermConfig(FILE *File) { PERM_CONFIG Config = (PERM_CONFIG) alloc_struct(sizeof(PERM_CONFIG_STRUCT), "PERM_CONFIG_STRUCT"); uinT8 NumAmbigs; fread ((char *) &NumAmbigs, sizeof(uinT8), 1, File); Config->Ambigs = new UNICHAR_ID[NumAmbigs + 1]; fread(Config->Ambigs, sizeof(UNICHAR_ID), NumAmbigs, File); Config->Ambigs[NumAmbigs] = -1; fread(&(Config->FontinfoId), sizeof(int), 1, File); return (Config); } /* ReadPermConfig */ /*---------------------------------------------------------------------------*/ /** * Read a temporary configuration description from File * and return a ptr to it. * * @param File open file to read temporary config from * @return Ptr to new temporary configuration description. * * @note Globals: none * @note Exceptions: none * @note History: Tue Mar 19 14:29:59 1991, DSJ, Created. */ TEMP_CONFIG ReadTempConfig(FILE *File) { TEMP_CONFIG Config; Config = (TEMP_CONFIG) alloc_struct (sizeof (TEMP_CONFIG_STRUCT), "TEMP_CONFIG_STRUCT"); fread ((char *) Config, sizeof (TEMP_CONFIG_STRUCT), 1, File); Config->Protos = NewBitVector (Config->ProtoVectorSize * BITSINLONG); fread ((char *) Config->Protos, sizeof (uinT32), Config->ProtoVectorSize, File); return (Config); } /* ReadTempConfig */ /*---------------------------------------------------------------------------*/ /** * This routine writes a binary representation of Class * to File. * * @param File open file to write Class to * @param Class adapted class to write to File * @param NumConfigs number of configs in Class * * @note Globals: none * @note Exceptions: none * @note History: Tue Mar 19 13:33:51 1991, DSJ, Created. */ void WriteAdaptedClass(FILE *File, ADAPT_CLASS Class, int NumConfigs) { int NumTempProtos; LIST TempProtos; int i; /* first write high level adapted class structure */ fwrite ((char *) Class, sizeof (ADAPT_CLASS_STRUCT), 1, File); /* then write out the definitions of the permanent protos and configs */ fwrite ((char *) Class->PermProtos, sizeof (uinT32), WordsInVectorOfSize (MAX_NUM_PROTOS), File); fwrite ((char *) Class->PermConfigs, sizeof (uinT32), WordsInVectorOfSize (MAX_NUM_CONFIGS), File); /* then write out the list of temporary protos */ NumTempProtos = count (Class->TempProtos); fwrite ((char *) &NumTempProtos, sizeof (int), 1, File); TempProtos = Class->TempProtos; iterate (TempProtos) { void* proto = first_node(TempProtos); fwrite ((char *) proto, sizeof (TEMP_PROTO_STRUCT), 1, File); } /* then write out the adapted configs */ fwrite ((char *) &NumConfigs, sizeof (int), 1, File); for (i = 0; i < NumConfigs; i++) if (test_bit (Class->PermConfigs, i)) WritePermConfig (File, Class->Config[i].Perm); else WriteTempConfig (File, Class->Config[i].Temp); } /* WriteAdaptedClass */ /*---------------------------------------------------------------------------*/ namespace tesseract { /** * This routine saves Templates to File in a binary format. * * @param File open text file to write Templates to * @param Templates set of adapted templates to write to File * * @note Globals: none * @note Exceptions: none * @note History: Mon Mar 18 15:07:32 1991, DSJ, Created. */ void Classify::WriteAdaptedTemplates(FILE *File, ADAPT_TEMPLATES Templates) { int i; /* first write the high level adaptive template struct */ fwrite ((char *) Templates, sizeof (ADAPT_TEMPLATES_STRUCT), 1, File); /* then write out the basic integer templates */ WriteIntTemplates (File, Templates->Templates, unicharset); /* then write out the adaptive info for each class */ for (i = 0; i < (Templates->Templates)->NumClasses; i++) { WriteAdaptedClass (File, Templates->Class[i], Templates->Templates->Class[i]->NumConfigs); } } /* WriteAdaptedTemplates */ } // namespace tesseract /*---------------------------------------------------------------------------*/ /** * This routine writes a binary representation of a * permanent configuration to File. * * @param File open file to write Config to * @param Config permanent config to write to File * * @note Globals: none * @note Exceptions: none * @note History: Tue Mar 19 13:55:44 1991, DSJ, Created. */ void WritePermConfig(FILE *File, PERM_CONFIG Config) { uinT8 NumAmbigs = 0; assert (Config != NULL); while (Config->Ambigs[NumAmbigs] > 0) ++NumAmbigs; fwrite((char *) &NumAmbigs, sizeof(uinT8), 1, File); fwrite(Config->Ambigs, sizeof(UNICHAR_ID), NumAmbigs, File); fwrite(&(Config->FontinfoId), sizeof(int), 1, File); } /* WritePermConfig */ /*---------------------------------------------------------------------------*/ /** * This routine writes a binary representation of a * temporary configuration to File. * * @param File open file to write Config to * @param Config temporary config to write to File * * @note Globals: none * @note Exceptions: none * @note History: Tue Mar 19 14:00:28 1991, DSJ, Created. */ void WriteTempConfig(FILE *File, TEMP_CONFIG Config) { assert (Config != NULL); /* contexts not yet implemented */ assert (Config->ContextsSeen == NULL); fwrite ((char *) Config, sizeof (TEMP_CONFIG_STRUCT), 1, File); fwrite ((char *) Config->Protos, sizeof (uinT32), Config->ProtoVectorSize, File); } /* WriteTempConfig */ tesseract-3.04.01/classify/adaptive.h000066400000000000000000000077731266071204500174600ustar00rootroot00000000000000/****************************************************************************** ** Filename: adaptive.h ** Purpose: Interface to adaptive matcher. ** Author: Dan Johnson ** History: Fri Mar 8 10:00:49 1991, 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 ADAPTIVE_H #define ADAPTIVE_H /*---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------*/ #include "oldlist.h" #include "intproto.h" #include typedef struct { uinT16 ProtoId; uinT16 dummy; PROTO_STRUCT Proto; } TEMP_PROTO_STRUCT; typedef TEMP_PROTO_STRUCT *TEMP_PROTO; typedef struct { uinT8 NumTimesSeen; uinT8 ProtoVectorSize; PROTO_ID MaxProtoId; LIST ContextsSeen; BIT_VECTOR Protos; int FontinfoId; // font information inferred from pre-trained templates } TEMP_CONFIG_STRUCT; typedef TEMP_CONFIG_STRUCT *TEMP_CONFIG; typedef struct { UNICHAR_ID *Ambigs; int FontinfoId; // font information inferred from pre-trained templates } PERM_CONFIG_STRUCT; typedef PERM_CONFIG_STRUCT *PERM_CONFIG; typedef union { TEMP_CONFIG Temp; PERM_CONFIG Perm; } ADAPTED_CONFIG; typedef struct { uinT8 NumPermConfigs; uinT8 MaxNumTimesSeen; // maximum number of times any TEMP_CONFIG was seen uinT8 dummy[2]; // (cut at matcher_min_examples_for_prototyping) BIT_VECTOR PermProtos; BIT_VECTOR PermConfigs; LIST TempProtos; ADAPTED_CONFIG Config[MAX_NUM_CONFIGS]; } ADAPT_CLASS_STRUCT; typedef ADAPT_CLASS_STRUCT *ADAPT_CLASS; typedef struct { INT_TEMPLATES Templates; int NumNonEmptyClasses; uinT8 NumPermClasses; uinT8 dummy[3]; ADAPT_CLASS Class[MAX_NUM_CLASSES]; } ADAPT_TEMPLATES_STRUCT; typedef ADAPT_TEMPLATES_STRUCT *ADAPT_TEMPLATES; /*---------------------------------------------------------------------------- Public Function Prototypes ----------------------------------------------------------------------------*/ #define NumNonEmptyClassesIn(Template) ((Template)->NumNonEmptyClasses) #define IsEmptyAdaptedClass(Class) ((Class)->NumPermConfigs == 0 && \ (Class)->TempProtos == NIL_LIST) #define ConfigIsPermanent(Class,ConfigId) \ (test_bit ((Class)->PermConfigs, ConfigId)) #define MakeConfigPermanent(Class,ConfigId) \ (SET_BIT ((Class)->PermConfigs, ConfigId)) #define MakeProtoPermanent(Class,ProtoId) \ (SET_BIT ((Class)->PermProtos, ProtoId)) #define TempConfigFor(Class,ConfigId) \ ((Class)->Config[ConfigId].Temp) #define PermConfigFor(Class,ConfigId) \ ((Class)->Config[ConfigId].Perm) #define IncreaseConfidence(TempConfig) \ ((TempConfig)->NumTimesSeen++) void AddAdaptedClass(ADAPT_TEMPLATES Templates, ADAPT_CLASS Class, CLASS_ID ClassId); void FreeTempProto(void *arg); void FreeTempConfig(TEMP_CONFIG Config); ADAPT_CLASS NewAdaptedClass(); void free_adapted_class(ADAPT_CLASS adapt_class); void free_adapted_templates(ADAPT_TEMPLATES templates); TEMP_CONFIG NewTempConfig(int MaxProtoId, int FontinfoId); TEMP_PROTO NewTempProto(); ADAPT_CLASS ReadAdaptedClass(FILE *File); PERM_CONFIG ReadPermConfig(FILE *File); TEMP_CONFIG ReadTempConfig(FILE *File); void WriteAdaptedClass(FILE *File, ADAPT_CLASS Class, int NumConfigs); void WritePermConfig(FILE *File, PERM_CONFIG Config); void WriteTempConfig(FILE *File, TEMP_CONFIG Config); #endif tesseract-3.04.01/classify/adaptmatch.cpp000066400000000000000000002612701266071204500203160ustar00rootroot00000000000000/****************************************************************************** ** Filename: adaptmatch.c ** Purpose: High level adaptive matcher. ** Author: Dan Johnson ** History: Mon Mar 11 10:00:10 1991, 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 -----------------------------------------------------------------------------*/ #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include #include "shapeclassifier.h" #include "ambigs.h" #include "blobclass.h" #include "blobs.h" #include "callcpp.h" #include "classify.h" #include "const.h" #include "dict.h" #include "efio.h" #include "emalloc.h" #include "featdefs.h" #include "float2int.h" #include "genericvector.h" #include "globals.h" #include "helpers.h" #include "intfx.h" #include "intproto.h" #include "mfoutline.h" #include "ndminx.h" #include "normfeat.h" #include "normmatch.h" #include "outfeat.h" #include "pageres.h" #include "params.h" #include "picofeat.h" #include "shapetable.h" #include "tessclassifier.h" #include "trainingsample.h" #include "unicharset.h" #include "werd.h" #include #include #include #include #ifdef __UNIX__ #include #endif #define ADAPT_TEMPLATE_SUFFIX ".a" #define MAX_MATCHES 10 #define UNLIKELY_NUM_FEAT 200 #define NO_DEBUG 0 #define MAX_ADAPTABLE_WERD_SIZE 40 #define ADAPTABLE_WERD_ADJUSTMENT (0.05) #define Y_DIM_OFFSET (Y_SHIFT - BASELINE_Y_SHIFT) #define WORST_POSSIBLE_RATING (0.0f) using tesseract::UnicharRating; using tesseract::ScoredFont; struct ADAPT_RESULTS { inT32 BlobLength; bool HasNonfragment; UNICHAR_ID best_unichar_id; int best_match_index; FLOAT32 best_rating; GenericVector match; GenericVector CPResults; /// Initializes data members to the default values. Sets the initial /// rating of each class to be the worst possible rating (1.0). inline void Initialize() { BlobLength = MAX_INT32; HasNonfragment = false; ComputeBest(); } // Computes best_unichar_id, best_match_index and best_rating. void ComputeBest() { best_unichar_id = INVALID_UNICHAR_ID; best_match_index = -1; best_rating = WORST_POSSIBLE_RATING; for (int i = 0; i < match.size(); ++i) { if (match[i].rating > best_rating) { best_rating = match[i].rating; best_unichar_id = match[i].unichar_id; best_match_index = i; } } } }; struct PROTO_KEY { ADAPT_TEMPLATES Templates; CLASS_ID ClassId; int ConfigId; }; /*----------------------------------------------------------------------------- Private Macros -----------------------------------------------------------------------------*/ inline bool MarginalMatch(float confidence, float matcher_great_threshold) { return (1.0f - confidence) > matcher_great_threshold; } /*----------------------------------------------------------------------------- Private Function Prototypes -----------------------------------------------------------------------------*/ // Returns the index of the given id in results, if present, or the size of the // vector (index it will go at) if not present. static int FindScoredUnichar(UNICHAR_ID id, const ADAPT_RESULTS& results) { for (int i = 0; i < results.match.size(); i++) { if (results.match[i].unichar_id == id) return i; } return results.match.size(); } // Returns the current rating for a unichar id if we have rated it, defaulting // to WORST_POSSIBLE_RATING. static float ScoredUnichar(UNICHAR_ID id, const ADAPT_RESULTS& results) { int index = FindScoredUnichar(id, results); if (index >= results.match.size()) return WORST_POSSIBLE_RATING; return results.match[index].rating; } void InitMatcherRatings(register FLOAT32 *Rating); int MakeTempProtoPerm(void *item1, void *item2); void SetAdaptiveThreshold(FLOAT32 Threshold); /*----------------------------------------------------------------------------- Public Code -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ namespace tesseract { /** * This routine calls the adaptive matcher * which returns (in an array) the class id of each * class matched. * * It also returns the number of classes matched. * For each class matched it places the best rating * found for that class into the Ratings array. * * Bad matches are then removed so that they don't * need to be sorted. The remaining good matches are * then sorted and converted to choices. * * This routine also performs some simple speckle * filtering. * * @note Exceptions: none * @note History: Mon Mar 11 10:00:58 1991, DSJ, Created. * * @param Blob blob to be classified * @param[out] Choices List of choices found by adaptive matcher. * filled on return with the choices found by the * class pruner and the ratings therefrom. Also * contains the detailed results of the integer matcher. * */ void Classify::AdaptiveClassifier(TBLOB *Blob, BLOB_CHOICE_LIST *Choices) { assert(Choices != NULL); ADAPT_RESULTS *Results = new ADAPT_RESULTS; Results->Initialize(); ASSERT_HOST(AdaptedTemplates != NULL); DoAdaptiveMatch(Blob, Results); RemoveBadMatches(Results); Results->match.sort(&UnicharRating::SortDescendingRating); RemoveExtraPuncs(Results); Results->ComputeBest(); ConvertMatchesToChoices(Blob->denorm(), Blob->bounding_box(), Results, Choices); // TODO(rays) Move to before ConvertMatchesToChoices! if (LargeSpeckle(*Blob) || Choices->length() == 0) AddLargeSpeckleTo(Results->BlobLength, Choices); if (matcher_debug_level >= 1) { tprintf("AD Matches = "); PrintAdaptiveMatchResults(*Results); } #ifndef GRAPHICS_DISABLED if (classify_enable_adaptive_debugger) DebugAdaptiveClassifier(Blob, Results); #endif delete Results; } /* AdaptiveClassifier */ // If *win is NULL, sets it to a new ScrollView() object with title msg. // Clears the window and draws baselines. void Classify::RefreshDebugWindow(ScrollView **win, const char *msg, int y_offset, const TBOX &wbox) { #ifndef GRAPHICS_DISABLED const int kSampleSpaceWidth = 500; if (*win == NULL) { *win = new ScrollView(msg, 100, y_offset, kSampleSpaceWidth * 2, 200, kSampleSpaceWidth * 2, 200, true); } (*win)->Clear(); (*win)->Pen(64, 64, 64); (*win)->Line(-kSampleSpaceWidth, kBlnBaselineOffset, kSampleSpaceWidth, kBlnBaselineOffset); (*win)->Line(-kSampleSpaceWidth, kBlnXHeight + kBlnBaselineOffset, kSampleSpaceWidth, kBlnXHeight + kBlnBaselineOffset); (*win)->ZoomToRectangle(wbox.left(), wbox.top(), wbox.right(), wbox.bottom()); #endif // GRAPHICS_DISABLED } // Learns the given word using its chopped_word, seam_array, denorm, // box_word, best_state, and correct_text to learn both correctly and // incorrectly segmented blobs. If fontname is not NULL, then LearnBlob // is called and the data will be saved in an internal buffer. // Otherwise AdaptToBlob is called for adaption within a document. void Classify::LearnWord(const char* fontname, WERD_RES* word) { int word_len = word->correct_text.size(); if (word_len == 0) return; float* thresholds = NULL; if (fontname == NULL) { // Adaption mode. if (!EnableLearning || word->best_choice == NULL) return; // Can't or won't adapt. if (classify_learning_debug_level >= 1) tprintf("\n\nAdapting to word = %s\n", word->best_choice->debug_string().string()); thresholds = new float[word_len]; word->ComputeAdaptionThresholds(certainty_scale, matcher_perfect_threshold, matcher_good_threshold, matcher_rating_margin, thresholds); } int start_blob = 0; #ifndef GRAPHICS_DISABLED if (classify_debug_character_fragments) { if (learn_fragmented_word_debug_win_ != NULL) { window_wait(learn_fragmented_word_debug_win_); } RefreshDebugWindow(&learn_fragments_debug_win_, "LearnPieces", 400, word->chopped_word->bounding_box()); RefreshDebugWindow(&learn_fragmented_word_debug_win_, "LearnWord", 200, word->chopped_word->bounding_box()); word->chopped_word->plot(learn_fragmented_word_debug_win_); ScrollView::Update(); } #endif // GRAPHICS_DISABLED for (int ch = 0; ch < word_len; ++ch) { if (classify_debug_character_fragments) { tprintf("\nLearning %s\n", word->correct_text[ch].string()); } if (word->correct_text[ch].length() > 0) { float threshold = thresholds != NULL ? thresholds[ch] : 0.0f; LearnPieces(fontname, start_blob, word->best_state[ch], threshold, CST_WHOLE, word->correct_text[ch].string(), word); if (word->best_state[ch] > 1 && !disable_character_fragments) { // Check that the character breaks into meaningful fragments // that each match a whole character with at least // classify_character_fragments_garbage_certainty_threshold bool garbage = false; int frag; for (frag = 0; frag < word->best_state[ch]; ++frag) { TBLOB* frag_blob = word->chopped_word->blobs[start_blob + frag]; if (classify_character_fragments_garbage_certainty_threshold < 0) { garbage |= LooksLikeGarbage(frag_blob); } } // Learn the fragments. if (!garbage) { bool pieces_all_natural = word->PiecesAllNatural(start_blob, word->best_state[ch]); if (pieces_all_natural || !prioritize_division) { for (frag = 0; frag < word->best_state[ch]; ++frag) { GenericVector tokens; word->correct_text[ch].split(' ', &tokens); tokens[0] = CHAR_FRAGMENT::to_string( tokens[0].string(), frag, word->best_state[ch], pieces_all_natural); STRING full_string; for (int i = 0; i < tokens.size(); i++) { full_string += tokens[i]; if (i != tokens.size() - 1) full_string += ' '; } LearnPieces(fontname, start_blob + frag, 1, threshold, CST_FRAGMENT, full_string.string(), word); } } } } // TODO(rays): re-enable this part of the code when we switch to the // new classifier that needs to see examples of garbage. /* if (word->best_state[ch] > 1) { // If the next blob is good, make junk with the rightmost fragment. if (ch + 1 < word_len && word->correct_text[ch + 1].length() > 0) { LearnPieces(fontname, start_blob + word->best_state[ch] - 1, word->best_state[ch + 1] + 1, threshold, CST_IMPROPER, INVALID_UNICHAR, word); } // If the previous blob is good, make junk with the leftmost fragment. if (ch > 0 && word->correct_text[ch - 1].length() > 0) { LearnPieces(fontname, start_blob - word->best_state[ch - 1], word->best_state[ch - 1] + 1, threshold, CST_IMPROPER, INVALID_UNICHAR, word); } } // If the next blob is good, make a join with it. if (ch + 1 < word_len && word->correct_text[ch + 1].length() > 0) { STRING joined_text = word->correct_text[ch]; joined_text += word->correct_text[ch + 1]; LearnPieces(fontname, start_blob, word->best_state[ch] + word->best_state[ch + 1], threshold, CST_NGRAM, joined_text.string(), word); } */ } start_blob += word->best_state[ch]; } delete [] thresholds; } // LearnWord. // Builds a blob of length fragments, from the word, starting at start, // and then learns it, as having the given correct_text. // If fontname is not NULL, then LearnBlob is called and the data will be // saved in an internal buffer for static training. // Otherwise AdaptToBlob is called for adaption within a document. // threshold is a magic number required by AdaptToChar and generated by // ComputeAdaptionThresholds. // Although it can be partly inferred from the string, segmentation is // provided to explicitly clarify the character segmentation. void Classify::LearnPieces(const char* fontname, int start, int length, float threshold, CharSegmentationType segmentation, const char* correct_text, WERD_RES* word) { // TODO(daria) Remove/modify this if/when we want // to train and/or adapt to n-grams. if (segmentation != CST_WHOLE && (segmentation != CST_FRAGMENT || disable_character_fragments)) return; if (length > 1) { SEAM::JoinPieces(word->seam_array, word->chopped_word->blobs, start, start + length - 1); } TBLOB* blob = word->chopped_word->blobs[start]; // Rotate the blob if needed for classification. TBLOB* rotated_blob = blob->ClassifyNormalizeIfNeeded(); if (rotated_blob == NULL) rotated_blob = blob; #ifndef GRAPHICS_DISABLED // Draw debug windows showing the blob that is being learned if needed. if (strcmp(classify_learn_debug_str.string(), correct_text) == 0) { RefreshDebugWindow(&learn_debug_win_, "LearnPieces", 600, word->chopped_word->bounding_box()); rotated_blob->plot(learn_debug_win_, ScrollView::GREEN, ScrollView::BROWN); learn_debug_win_->Update(); window_wait(learn_debug_win_); } if (classify_debug_character_fragments && segmentation == CST_FRAGMENT) { ASSERT_HOST(learn_fragments_debug_win_ != NULL); // set up in LearnWord blob->plot(learn_fragments_debug_win_, ScrollView::BLUE, ScrollView::BROWN); learn_fragments_debug_win_->Update(); } #endif // GRAPHICS_DISABLED if (fontname != NULL) { classify_norm_method.set_value(character); // force char norm spc 30/11/93 tess_bn_matching.set_value(false); // turn it off tess_cn_matching.set_value(false); DENORM bl_denorm, cn_denorm; INT_FX_RESULT_STRUCT fx_info; SetupBLCNDenorms(*rotated_blob, classify_nonlinear_norm, &bl_denorm, &cn_denorm, &fx_info); LearnBlob(fontname, rotated_blob, cn_denorm, fx_info, correct_text); } else if (unicharset.contains_unichar(correct_text)) { UNICHAR_ID class_id = unicharset.unichar_to_id(correct_text); int font_id = word->fontinfo != NULL ? fontinfo_table_.get_id(*word->fontinfo) : 0; if (classify_learning_debug_level >= 1) tprintf("Adapting to char = %s, thr= %g font_id= %d\n", unicharset.id_to_unichar(class_id), threshold, font_id); // If filename is not NULL we are doing recognition // (as opposed to training), so we must have already set word fonts. AdaptToChar(rotated_blob, class_id, font_id, threshold, AdaptedTemplates); if (BackupAdaptedTemplates != NULL) { // Adapt the backup templates too. They will be used if the primary gets // too full. AdaptToChar(rotated_blob, class_id, font_id, threshold, BackupAdaptedTemplates); } } else if (classify_debug_level >= 1) { tprintf("Can't adapt to %s not in unicharset\n", correct_text); } if (rotated_blob != blob) { delete rotated_blob; } SEAM::BreakPieces(word->seam_array, word->chopped_word->blobs, start, start + length - 1); } // LearnPieces. /*---------------------------------------------------------------------------*/ /** * This routine performs cleanup operations * on the adaptive classifier. It should be called * before the program is terminated. Its main function * is to save the adapted templates to a file. * * Globals: * - #AdaptedTemplates current set of adapted templates * - #classify_save_adapted_templates TRUE if templates should be saved * - #classify_enable_adaptive_matcher TRUE if adaptive matcher is enabled * * @note Exceptions: none * @note History: Tue Mar 19 14:37:06 1991, DSJ, Created. */ void Classify::EndAdaptiveClassifier() { STRING Filename; FILE *File; if (AdaptedTemplates != NULL && classify_enable_adaptive_matcher && classify_save_adapted_templates) { Filename = imagefile + ADAPT_TEMPLATE_SUFFIX; File = fopen (Filename.string(), "wb"); if (File == NULL) cprintf ("Unable to save adapted templates to %s!\n", Filename.string()); else { cprintf ("\nSaving adapted templates to %s ...", Filename.string()); fflush(stdout); WriteAdaptedTemplates(File, AdaptedTemplates); cprintf ("\n"); fclose(File); } } if (AdaptedTemplates != NULL) { free_adapted_templates(AdaptedTemplates); AdaptedTemplates = NULL; } if (BackupAdaptedTemplates != NULL) { free_adapted_templates(BackupAdaptedTemplates); BackupAdaptedTemplates = NULL; } if (PreTrainedTemplates != NULL) { free_int_templates(PreTrainedTemplates); PreTrainedTemplates = NULL; } getDict().EndDangerousAmbigs(); FreeNormProtos(); if (AllProtosOn != NULL) { FreeBitVector(AllProtosOn); FreeBitVector(AllConfigsOn); FreeBitVector(AllConfigsOff); FreeBitVector(TempProtoMask); AllProtosOn = NULL; AllConfigsOn = NULL; AllConfigsOff = NULL; TempProtoMask = NULL; } delete shape_table_; shape_table_ = NULL; if (static_classifier_ != NULL) { delete static_classifier_; static_classifier_ = NULL; } } /* EndAdaptiveClassifier */ /*---------------------------------------------------------------------------*/ /** * This routine reads in the training * information needed by the adaptive classifier * and saves it into global variables. * Parameters: * load_pre_trained_templates Indicates whether the pre-trained * templates (inttemp, normproto and pffmtable components) * should be lodaded. Should only be set to true if the * necessary classifier components are present in the * [lang].traineddata file. * Globals: * BuiltInTemplatesFile file to get built-in temps from * BuiltInCutoffsFile file to get avg. feat per class from * classify_use_pre_adapted_templates * enables use of pre-adapted templates * @note History: Mon Mar 11 12:49:34 1991, DSJ, Created. */ void Classify::InitAdaptiveClassifier(bool load_pre_trained_templates) { if (!classify_enable_adaptive_matcher) return; if (AllProtosOn != NULL) EndAdaptiveClassifier(); // Don't leak with multiple inits. // If there is no language_data_path_prefix, the classifier will be // adaptive only. if (language_data_path_prefix.length() > 0 && load_pre_trained_templates) { ASSERT_HOST(tessdata_manager.SeekToStart(TESSDATA_INTTEMP)); PreTrainedTemplates = ReadIntTemplates(tessdata_manager.GetDataFilePtr()); if (tessdata_manager.DebugLevel() > 0) tprintf("Loaded inttemp\n"); if (tessdata_manager.SeekToStart(TESSDATA_SHAPE_TABLE)) { shape_table_ = new ShapeTable(unicharset); if (!shape_table_->DeSerialize(tessdata_manager.swap(), tessdata_manager.GetDataFilePtr())) { tprintf("Error loading shape table!\n"); delete shape_table_; shape_table_ = NULL; } else if (tessdata_manager.DebugLevel() > 0) { tprintf("Successfully loaded shape table!\n"); } } ASSERT_HOST(tessdata_manager.SeekToStart(TESSDATA_PFFMTABLE)); ReadNewCutoffs(tessdata_manager.GetDataFilePtr(), tessdata_manager.swap(), tessdata_manager.GetEndOffset(TESSDATA_PFFMTABLE), CharNormCutoffs); if (tessdata_manager.DebugLevel() > 0) tprintf("Loaded pffmtable\n"); ASSERT_HOST(tessdata_manager.SeekToStart(TESSDATA_NORMPROTO)); NormProtos = ReadNormProtos(tessdata_manager.GetDataFilePtr(), tessdata_manager.GetEndOffset(TESSDATA_NORMPROTO)); if (tessdata_manager.DebugLevel() > 0) tprintf("Loaded normproto\n"); static_classifier_ = new TessClassifier(false, this); } im_.Init(&classify_debug_level); InitIntegerFX(); AllProtosOn = NewBitVector(MAX_NUM_PROTOS); AllConfigsOn = NewBitVector(MAX_NUM_CONFIGS); AllConfigsOff = NewBitVector(MAX_NUM_CONFIGS); TempProtoMask = NewBitVector(MAX_NUM_PROTOS); set_all_bits(AllProtosOn, WordsInVectorOfSize(MAX_NUM_PROTOS)); set_all_bits(AllConfigsOn, WordsInVectorOfSize(MAX_NUM_CONFIGS)); zero_all_bits(AllConfigsOff, WordsInVectorOfSize(MAX_NUM_CONFIGS)); for (int i = 0; i < MAX_NUM_CLASSES; i++) { BaselineCutoffs[i] = 0; } if (classify_use_pre_adapted_templates) { FILE *File; STRING Filename; Filename = imagefile; Filename += ADAPT_TEMPLATE_SUFFIX; File = fopen(Filename.string(), "rb"); if (File == NULL) { AdaptedTemplates = NewAdaptedTemplates(true); } else { cprintf("\nReading pre-adapted templates from %s ...\n", Filename.string()); fflush(stdout); AdaptedTemplates = ReadAdaptedTemplates(File); cprintf("\n"); fclose(File); PrintAdaptedTemplates(stdout, AdaptedTemplates); for (int i = 0; i < AdaptedTemplates->Templates->NumClasses; i++) { BaselineCutoffs[i] = CharNormCutoffs[i]; } } } else { if (AdaptedTemplates != NULL) free_adapted_templates(AdaptedTemplates); AdaptedTemplates = NewAdaptedTemplates(true); } } /* InitAdaptiveClassifier */ void Classify::ResetAdaptiveClassifierInternal() { if (classify_learning_debug_level > 0) { tprintf("Resetting adaptive classifier (NumAdaptationsFailed=%d)\n", NumAdaptationsFailed); } free_adapted_templates(AdaptedTemplates); AdaptedTemplates = NewAdaptedTemplates(true); if (BackupAdaptedTemplates != NULL) free_adapted_templates(BackupAdaptedTemplates); BackupAdaptedTemplates = NULL; NumAdaptationsFailed = 0; } // If there are backup adapted templates, switches to those, otherwise resets // the main adaptive classifier (because it is full.) void Classify::SwitchAdaptiveClassifier() { if (BackupAdaptedTemplates == NULL) { ResetAdaptiveClassifierInternal(); return; } if (classify_learning_debug_level > 0) { tprintf("Switch to backup adaptive classifier (NumAdaptationsFailed=%d)\n", NumAdaptationsFailed); } free_adapted_templates(AdaptedTemplates); AdaptedTemplates = BackupAdaptedTemplates; BackupAdaptedTemplates = NULL; NumAdaptationsFailed = 0; } // Resets the backup adaptive classifier to empty. void Classify::StartBackupAdaptiveClassifier() { if (BackupAdaptedTemplates != NULL) free_adapted_templates(BackupAdaptedTemplates); BackupAdaptedTemplates = NewAdaptedTemplates(true); } /*---------------------------------------------------------------------------*/ /** * This routine prepares the adaptive * matcher for the start * of the first pass. Learning is enabled (unless it * is disabled for the whole program). * * @note this is somewhat redundant, it simply says that if learning is * enabled then it will remain enabled on the first pass. If it is * disabled, then it will remain disabled. This is only put here to * make it very clear that learning is controlled directly by the global * setting of EnableLearning. * * Globals: * - #EnableLearning * set to TRUE by this routine * * @note Exceptions: none * @note History: Mon Apr 15 16:39:29 1991, DSJ, Created. */ void Classify::SettupPass1() { EnableLearning = classify_enable_learning; getDict().SettupStopperPass1(); } /* SettupPass1 */ /*---------------------------------------------------------------------------*/ /** * This routine prepares the adaptive * matcher for the start of the second pass. Further * learning is disabled. * * Globals: * - #EnableLearning set to FALSE by this routine * * @note Exceptions: none * @note History: Mon Apr 15 16:39:29 1991, DSJ, Created. */ void Classify::SettupPass2() { EnableLearning = FALSE; getDict().SettupStopperPass2(); } /* SettupPass2 */ /*---------------------------------------------------------------------------*/ /** * This routine creates a new adapted * class and uses Blob as the model for the first * config in that class. * * @param Blob blob to model new class after * @param ClassId id of the class to be initialized * @param FontinfoId font information inferred from pre-trained templates * @param Class adapted class to be initialized * @param Templates adapted templates to add new class to * * Globals: * - #AllProtosOn dummy mask with all 1's * - BaselineCutoffs kludge needed to get cutoffs * - #PreTrainedTemplates kludge needed to get cutoffs * * @note Exceptions: none * @note History: Thu Mar 14 12:49:39 1991, DSJ, Created. */ void Classify::InitAdaptedClass(TBLOB *Blob, CLASS_ID ClassId, int FontinfoId, ADAPT_CLASS Class, ADAPT_TEMPLATES Templates) { FEATURE_SET Features; int Fid, Pid; FEATURE Feature; int NumFeatures; TEMP_PROTO TempProto; PROTO Proto; INT_CLASS IClass; TEMP_CONFIG Config; classify_norm_method.set_value(baseline); Features = ExtractOutlineFeatures(Blob); NumFeatures = Features->NumFeatures; if (NumFeatures > UNLIKELY_NUM_FEAT || NumFeatures <= 0) { FreeFeatureSet(Features); return; } Config = NewTempConfig(NumFeatures - 1, FontinfoId); TempConfigFor(Class, 0) = Config; /* this is a kludge to construct cutoffs for adapted templates */ if (Templates == AdaptedTemplates) BaselineCutoffs[ClassId] = CharNormCutoffs[ClassId]; IClass = ClassForClassId (Templates->Templates, ClassId); for (Fid = 0; Fid < Features->NumFeatures; Fid++) { Pid = AddIntProto (IClass); assert (Pid != NO_PROTO); Feature = Features->Features[Fid]; TempProto = NewTempProto (); Proto = &(TempProto->Proto); /* compute proto params - NOTE that Y_DIM_OFFSET must be used because ConvertProto assumes that the Y dimension varies from -0.5 to 0.5 instead of the -0.25 to 0.75 used in baseline normalization */ Proto->Angle = Feature->Params[OutlineFeatDir]; Proto->X = Feature->Params[OutlineFeatX]; Proto->Y = Feature->Params[OutlineFeatY] - Y_DIM_OFFSET; Proto->Length = Feature->Params[OutlineFeatLength]; FillABC(Proto); TempProto->ProtoId = Pid; SET_BIT (Config->Protos, Pid); ConvertProto(Proto, Pid, IClass); AddProtoToProtoPruner(Proto, Pid, IClass, classify_learning_debug_level >= 2); Class->TempProtos = push (Class->TempProtos, TempProto); } FreeFeatureSet(Features); AddIntConfig(IClass); ConvertConfig (AllProtosOn, 0, IClass); if (classify_learning_debug_level >= 1) { tprintf("Added new class '%s' with class id %d and %d protos.\n", unicharset.id_to_unichar(ClassId), ClassId, NumFeatures); if (classify_learning_debug_level > 1) DisplayAdaptedChar(Blob, IClass); } if (IsEmptyAdaptedClass(Class)) (Templates->NumNonEmptyClasses)++; } /* InitAdaptedClass */ /*---------------------------------------------------------------------------*/ /** * This routine sets up the feature * extractor to extract baseline normalized * pico-features. * * The extracted pico-features are converted * to integer form and placed in IntFeatures. The * original floating-pt. features are returned in * FloatFeatures. * * Globals: none * @param Blob blob to extract features from * @param[out] IntFeatures array to fill with integer features * @param[out] FloatFeatures place to return actual floating-pt features * * @return Number of pico-features returned (0 if * an error occurred) * @note Exceptions: none * @note History: Tue Mar 12 17:55:18 1991, DSJ, Created. */ int Classify::GetAdaptiveFeatures(TBLOB *Blob, INT_FEATURE_ARRAY IntFeatures, FEATURE_SET *FloatFeatures) { FEATURE_SET Features; int NumFeatures; classify_norm_method.set_value(baseline); Features = ExtractPicoFeatures(Blob); NumFeatures = Features->NumFeatures; if (NumFeatures > UNLIKELY_NUM_FEAT) { FreeFeatureSet(Features); return 0; } ComputeIntFeatures(Features, IntFeatures); *FloatFeatures = Features; return NumFeatures; } /* GetAdaptiveFeatures */ /*----------------------------------------------------------------------------- Private Code -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /** * Return TRUE if the specified word is * acceptable for adaptation. * * Globals: none * * @param word current word * * @return TRUE or FALSE * @note Exceptions: none * @note History: Thu May 30 14:25:06 1991, DSJ, Created. */ bool Classify::AdaptableWord(WERD_RES* word) { if (word->best_choice == NULL) return false; int BestChoiceLength = word->best_choice->length(); float adaptable_score = getDict().segment_penalty_dict_case_ok + ADAPTABLE_WERD_ADJUSTMENT; return // rules that apply in general - simplest to compute first BestChoiceLength > 0 && BestChoiceLength == word->rebuild_word->NumBlobs() && BestChoiceLength <= MAX_ADAPTABLE_WERD_SIZE && // This basically ensures that the word is at least a dictionary match // (freq word, user word, system dawg word, etc). // Since all the other adjustments will make adjust factor higher // than higher than adaptable_score=1.1+0.05=1.15 // Since these are other flags that ensure that the word is dict word, // this check could be at times redundant. word->best_choice->adjust_factor() <= adaptable_score && // Make sure that alternative choices are not dictionary words. word->AlternativeChoiceAdjustmentsWorseThan(adaptable_score); } /*---------------------------------------------------------------------------*/ /** * @param Blob blob to add to templates for ClassId * @param ClassId class to add blob to * @param FontinfoId font information from pre-trained templates * @param Threshold minimum match rating to existing template * @param adaptive_templates current set of adapted templates * * Globals: * - AllProtosOn dummy mask to match against all protos * - AllConfigsOn dummy mask to match against all configs * * @return none * @note Exceptions: none * @note History: Thu Mar 14 09:36:03 1991, DSJ, Created. */ void Classify::AdaptToChar(TBLOB* Blob, CLASS_ID ClassId, int FontinfoId, FLOAT32 Threshold, ADAPT_TEMPLATES adaptive_templates) { int NumFeatures; INT_FEATURE_ARRAY IntFeatures; UnicharRating int_result; INT_CLASS IClass; ADAPT_CLASS Class; TEMP_CONFIG TempConfig; FEATURE_SET FloatFeatures; int NewTempConfigId; if (!LegalClassId (ClassId)) return; int_result.unichar_id = ClassId; Class = adaptive_templates->Class[ClassId]; assert(Class != NULL); if (IsEmptyAdaptedClass(Class)) { InitAdaptedClass(Blob, ClassId, FontinfoId, Class, adaptive_templates); } else { IClass = ClassForClassId(adaptive_templates->Templates, ClassId); NumFeatures = GetAdaptiveFeatures(Blob, IntFeatures, &FloatFeatures); if (NumFeatures <= 0) return; // Only match configs with the matching font. BIT_VECTOR MatchingFontConfigs = NewBitVector(MAX_NUM_PROTOS); for (int cfg = 0; cfg < IClass->NumConfigs; ++cfg) { if (GetFontinfoId(Class, cfg) == FontinfoId) { SET_BIT(MatchingFontConfigs, cfg); } else { reset_bit(MatchingFontConfigs, cfg); } } im_.Match(IClass, AllProtosOn, MatchingFontConfigs, NumFeatures, IntFeatures, &int_result, classify_adapt_feature_threshold, NO_DEBUG, matcher_debug_separate_windows); FreeBitVector(MatchingFontConfigs); SetAdaptiveThreshold(Threshold); if (1.0f - int_result.rating <= Threshold) { if (ConfigIsPermanent(Class, int_result.config)) { if (classify_learning_debug_level >= 1) tprintf("Found good match to perm config %d = %4.1f%%.\n", int_result.config, int_result.rating * 100.0); FreeFeatureSet(FloatFeatures); return; } TempConfig = TempConfigFor(Class, int_result.config); IncreaseConfidence(TempConfig); if (TempConfig->NumTimesSeen > Class->MaxNumTimesSeen) { Class->MaxNumTimesSeen = TempConfig->NumTimesSeen; } if (classify_learning_debug_level >= 1) tprintf("Increasing reliability of temp config %d to %d.\n", int_result.config, TempConfig->NumTimesSeen); if (TempConfigReliable(ClassId, TempConfig)) { MakePermanent(adaptive_templates, ClassId, int_result.config, Blob); UpdateAmbigsGroup(ClassId, Blob); } } else { if (classify_learning_debug_level >= 1) { tprintf("Found poor match to temp config %d = %4.1f%%.\n", int_result.config, int_result.rating * 100.0); if (classify_learning_debug_level > 2) DisplayAdaptedChar(Blob, IClass); } NewTempConfigId = MakeNewTemporaryConfig(adaptive_templates, ClassId, FontinfoId, NumFeatures, IntFeatures, FloatFeatures); if (NewTempConfigId >= 0 && TempConfigReliable(ClassId, TempConfigFor(Class, NewTempConfigId))) { MakePermanent(adaptive_templates, ClassId, NewTempConfigId, Blob); UpdateAmbigsGroup(ClassId, Blob); } #ifndef GRAPHICS_DISABLED if (classify_learning_debug_level > 1) { DisplayAdaptedChar(Blob, IClass); } #endif } FreeFeatureSet(FloatFeatures); } } /* AdaptToChar */ void Classify::DisplayAdaptedChar(TBLOB* blob, INT_CLASS_STRUCT* int_class) { #ifndef GRAPHICS_DISABLED INT_FX_RESULT_STRUCT fx_info; GenericVector bl_features; TrainingSample* sample = BlobToTrainingSample(*blob, classify_nonlinear_norm, &fx_info, &bl_features); if (sample == NULL) return; UnicharRating int_result; im_.Match(int_class, AllProtosOn, AllConfigsOn, bl_features.size(), &bl_features[0], &int_result, classify_adapt_feature_threshold, NO_DEBUG, matcher_debug_separate_windows); tprintf("Best match to temp config %d = %4.1f%%.\n", int_result.config, int_result.rating * 100.0); if (classify_learning_debug_level >= 2) { uinT32 ConfigMask; ConfigMask = 1 << int_result.config; ShowMatchDisplay(); im_.Match(int_class, AllProtosOn, (BIT_VECTOR)&ConfigMask, bl_features.size(), &bl_features[0], &int_result, classify_adapt_feature_threshold, 6 | 0x19, matcher_debug_separate_windows); UpdateMatchDisplay(); } #endif } /** * This routine adds the result of a classification into * Results. If the new rating is much worse than the current * best rating, it is not entered into results because it * would end up being stripped later anyway. If the new rating * is better than the old rating for the class, it replaces the * old rating. If this is the first rating for the class, the * class is added to the list of matched classes in Results. * If the new rating is better than the best so far, it * becomes the best so far. * * Globals: * - #matcher_bad_match_pad defines limits of an acceptable match * * @param new_result new result to add * @param[out] results results to add new result to * * @note Exceptions: none * @note History: Tue Mar 12 18:19:29 1991, DSJ, Created. */ void Classify::AddNewResult(const UnicharRating& new_result, ADAPT_RESULTS *results) { int old_match = FindScoredUnichar(new_result.unichar_id, *results); if (new_result.rating + matcher_bad_match_pad < results->best_rating || (old_match < results->match.size() && new_result.rating <= results->match[old_match].rating)) return; // New one not good enough. if (!unicharset.get_fragment(new_result.unichar_id)) results->HasNonfragment = true; if (old_match < results->match.size()) { results->match[old_match].rating = new_result.rating; } else { results->match.push_back(new_result); } if (new_result.rating > results->best_rating && // Ensure that fragments do not affect best rating, class and config. // This is needed so that at least one non-fragmented character is // always present in the results. // TODO(daria): verify that this helps accuracy and does not // hurt performance. !unicharset.get_fragment(new_result.unichar_id)) { results->best_match_index = old_match; results->best_rating = new_result.rating; results->best_unichar_id = new_result.unichar_id; } } /* AddNewResult */ /*---------------------------------------------------------------------------*/ /** * This routine is identical to CharNormClassifier() * except that it does no class pruning. It simply matches * the unknown blob against the classes listed in * Ambiguities. * * Globals: * - #AllProtosOn mask that enables all protos * - #AllConfigsOn mask that enables all configs * * @param blob blob to be classified * @param templates built-in templates to classify against * @param classes adapted class templates * @param ambiguities array of unichar id's to match against * @param[out] results place to put match results * @param int_features * @param fx_info * * @note Exceptions: none * @note History: Tue Mar 12 19:40:36 1991, DSJ, Created. */ void Classify::AmbigClassifier( const GenericVector& int_features, const INT_FX_RESULT_STRUCT& fx_info, const TBLOB *blob, INT_TEMPLATES templates, ADAPT_CLASS *classes, UNICHAR_ID *ambiguities, ADAPT_RESULTS *results) { if (int_features.empty()) return; uinT8* CharNormArray = new uinT8[unicharset.size()]; UnicharRating int_result; results->BlobLength = GetCharNormFeature(fx_info, templates, NULL, CharNormArray); bool debug = matcher_debug_level >= 2 || classify_debug_level > 1; if (debug) tprintf("AM Matches = "); int top = blob->bounding_box().top(); int bottom = blob->bounding_box().bottom(); while (*ambiguities >= 0) { CLASS_ID class_id = *ambiguities; int_result.unichar_id = class_id; im_.Match(ClassForClassId(templates, class_id), AllProtosOn, AllConfigsOn, int_features.size(), &int_features[0], &int_result, classify_adapt_feature_threshold, NO_DEBUG, matcher_debug_separate_windows); ExpandShapesAndApplyCorrections(NULL, debug, class_id, bottom, top, 0, results->BlobLength, classify_integer_matcher_multiplier, CharNormArray, &int_result, results); ambiguities++; } delete [] CharNormArray; } /* AmbigClassifier */ /*---------------------------------------------------------------------------*/ /// Factored-out calls to IntegerMatcher based on class pruner results. /// Returns integer matcher results inside CLASS_PRUNER_RESULTS structure. void Classify::MasterMatcher(INT_TEMPLATES templates, inT16 num_features, const INT_FEATURE_STRUCT* features, const uinT8* norm_factors, ADAPT_CLASS* classes, int debug, int matcher_multiplier, const TBOX& blob_box, const GenericVector& results, ADAPT_RESULTS* final_results) { int top = blob_box.top(); int bottom = blob_box.bottom(); UnicharRating int_result; for (int c = 0; c < results.size(); c++) { CLASS_ID class_id = results[c].Class; BIT_VECTOR protos = classes != NULL ? classes[class_id]->PermProtos : AllProtosOn; BIT_VECTOR configs = classes != NULL ? classes[class_id]->PermConfigs : AllConfigsOn; int_result.unichar_id = class_id; im_.Match(ClassForClassId(templates, class_id), protos, configs, num_features, features, &int_result, classify_adapt_feature_threshold, debug, matcher_debug_separate_windows); bool debug = matcher_debug_level >= 2 || classify_debug_level > 1; ExpandShapesAndApplyCorrections(classes, debug, class_id, bottom, top, results[c].Rating, final_results->BlobLength, matcher_multiplier, norm_factors, &int_result, final_results); } } // Converts configs to fonts, and if the result is not adapted, and a // shape_table_ is present, the shape is expanded to include all // unichar_ids represented, before applying a set of corrections to the // distance rating in int_result, (see ComputeCorrectedRating.) // The results are added to the final_results output. void Classify::ExpandShapesAndApplyCorrections( ADAPT_CLASS* classes, bool debug, int class_id, int bottom, int top, float cp_rating, int blob_length, int matcher_multiplier, const uinT8* cn_factors, UnicharRating* int_result, ADAPT_RESULTS* final_results) { if (classes != NULL) { // Adapted result. Convert configs to fontinfo_ids. int_result->adapted = true; for (int f = 0; f < int_result->fonts.size(); ++f) { int_result->fonts[f].fontinfo_id = GetFontinfoId(classes[class_id], int_result->fonts[f].fontinfo_id); } } else { // Pre-trained result. Map fonts using font_sets_. int_result->adapted = false; for (int f = 0; f < int_result->fonts.size(); ++f) { int_result->fonts[f].fontinfo_id = ClassAndConfigIDToFontOrShapeID(class_id, int_result->fonts[f].fontinfo_id); } if (shape_table_ != NULL) { // Two possible cases: // 1. Flat shapetable. All unichar-ids of the shapes referenced by // int_result->fonts are the same. In this case build a new vector of // mapped fonts and replace the fonts in int_result. // 2. Multi-unichar shapetable. Variable unichars in the shapes referenced // by int_result. In this case, build a vector of UnicharRating to // gather together different font-ids for each unichar. Also covers case1. GenericVector mapped_results; for (int f = 0; f < int_result->fonts.size(); ++f) { int shape_id = int_result->fonts[f].fontinfo_id; const Shape& shape = shape_table_->GetShape(shape_id); for (int c = 0; c < shape.size(); ++c) { int unichar_id = shape[c].unichar_id; if (!unicharset.get_enabled(unichar_id)) continue; // Find the mapped_result for unichar_id. int r = 0; for (r = 0; r < mapped_results.size() && mapped_results[r].unichar_id != unichar_id; ++r) {} if (r == mapped_results.size()) { mapped_results.push_back(*int_result); mapped_results[r].unichar_id = unichar_id; mapped_results[r].fonts.truncate(0); } for (int i = 0; i < shape[c].font_ids.size(); ++i) { mapped_results[r].fonts.push_back( ScoredFont(shape[c].font_ids[i], int_result->fonts[f].score)); } } } for (int m = 0; m < mapped_results.size(); ++m) { mapped_results[m].rating = ComputeCorrectedRating(debug, mapped_results[m].unichar_id, cp_rating, int_result->rating, int_result->feature_misses, bottom, top, blob_length, matcher_multiplier, cn_factors); AddNewResult(mapped_results[m], final_results); } return; } } if (unicharset.get_enabled(class_id)) { int_result->rating = ComputeCorrectedRating(debug, class_id, cp_rating, int_result->rating, int_result->feature_misses, bottom, top, blob_length, matcher_multiplier, cn_factors); AddNewResult(*int_result, final_results); } } // Applies a set of corrections to the confidence im_rating, // including the cn_correction, miss penalty and additional penalty // for non-alnums being vertical misfits. Returns the corrected confidence. double Classify::ComputeCorrectedRating(bool debug, int unichar_id, double cp_rating, double im_rating, int feature_misses, int bottom, int top, int blob_length, int matcher_multiplier, const uinT8* cn_factors) { // Compute class feature corrections. double cn_corrected = im_.ApplyCNCorrection(1.0 - im_rating, blob_length, cn_factors[unichar_id], matcher_multiplier); double miss_penalty = tessedit_class_miss_scale * feature_misses; double vertical_penalty = 0.0; // Penalize non-alnums for being vertical misfits. if (!unicharset.get_isalpha(unichar_id) && !unicharset.get_isdigit(unichar_id) && cn_factors[unichar_id] != 0 && classify_misfit_junk_penalty > 0.0) { int min_bottom, max_bottom, min_top, max_top; unicharset.get_top_bottom(unichar_id, &min_bottom, &max_bottom, &min_top, &max_top); if (debug) { tprintf("top=%d, vs [%d, %d], bottom=%d, vs [%d, %d]\n", top, min_top, max_top, bottom, min_bottom, max_bottom); } if (top < min_top || top > max_top || bottom < min_bottom || bottom > max_bottom) { vertical_penalty = classify_misfit_junk_penalty; } } double result = 1.0 - (cn_corrected + miss_penalty + vertical_penalty); if (result < WORST_POSSIBLE_RATING) result = WORST_POSSIBLE_RATING; if (debug) { tprintf("%s: %2.1f%%(CP%2.1f, IM%2.1f + CN%.2f(%d) + MP%2.1f + VP%2.1f)\n", unicharset.id_to_unichar(unichar_id), result * 100.0, cp_rating * 100.0, (1.0 - im_rating) * 100.0, (cn_corrected - (1.0 - im_rating)) * 100.0, cn_factors[unichar_id], miss_penalty * 100.0, vertical_penalty * 100.0); } return result; } /*---------------------------------------------------------------------------*/ /** * This routine extracts baseline normalized features * from the unknown character and matches them against the * specified set of templates. The classes which match * are added to Results. * * Globals: * - BaselineCutoffs expected num features for each class * * @param Blob blob to be classified * @param Templates current set of adapted templates * @param Results place to put match results * @param int_features * @param fx_info * * @return Array of possible ambiguous chars that should be checked. * @note Exceptions: none * @note History: Tue Mar 12 19:38:03 1991, DSJ, Created. */ UNICHAR_ID *Classify::BaselineClassifier( TBLOB *Blob, const GenericVector& int_features, const INT_FX_RESULT_STRUCT& fx_info, ADAPT_TEMPLATES Templates, ADAPT_RESULTS *Results) { if (int_features.empty()) return NULL; uinT8* CharNormArray = new uinT8[unicharset.size()]; ClearCharNormArray(CharNormArray); Results->BlobLength = IntCastRounded(fx_info.Length / kStandardFeatureLength); PruneClasses(Templates->Templates, int_features.size(), -1, &int_features[0], CharNormArray, BaselineCutoffs, &Results->CPResults); if (matcher_debug_level >= 2 || classify_debug_level > 1) tprintf("BL Matches = "); MasterMatcher(Templates->Templates, int_features.size(), &int_features[0], CharNormArray, Templates->Class, matcher_debug_flags, 0, Blob->bounding_box(), Results->CPResults, Results); delete [] CharNormArray; CLASS_ID ClassId = Results->best_unichar_id; if (ClassId == INVALID_UNICHAR_ID || Results->best_match_index < 0) return NULL; return Templates->Class[ClassId]-> Config[Results->match[Results->best_match_index].config].Perm->Ambigs; } /* BaselineClassifier */ /*---------------------------------------------------------------------------*/ /** * This routine extracts character normalized features * from the unknown character and matches them against the * specified set of templates. The classes which match * are added to Results. * * @param blob blob to be classified * @param sample templates to classify unknown against * @param adapt_results place to put match results * * Globals: * - CharNormCutoffs expected num features for each class * - AllProtosOn mask that enables all protos * - AllConfigsOn mask that enables all configs * * @note Exceptions: none * @note History: Tue Mar 12 16:02:52 1991, DSJ, Created. */ int Classify::CharNormClassifier(TBLOB *blob, const TrainingSample& sample, ADAPT_RESULTS *adapt_results) { // This is the length that is used for scaling ratings vs certainty. adapt_results->BlobLength = IntCastRounded(sample.outline_length() / kStandardFeatureLength); GenericVector unichar_results; static_classifier_->UnicharClassifySample(sample, blob->denorm().pix(), 0, -1, &unichar_results); // Convert results to the format used internally by AdaptiveClassifier. for (int r = 0; r < unichar_results.size(); ++r) { AddNewResult(unichar_results[r], adapt_results); } return sample.num_features(); } /* CharNormClassifier */ // As CharNormClassifier, but operates on a TrainingSample and outputs to // a GenericVector of ShapeRating without conversion to classes. int Classify::CharNormTrainingSample(bool pruner_only, int keep_this, const TrainingSample& sample, GenericVector* results) { results->clear(); ADAPT_RESULTS* adapt_results = new ADAPT_RESULTS(); adapt_results->Initialize(); // Compute the bounding box of the features. int num_features = sample.num_features(); // Only the top and bottom of the blob_box are used by MasterMatcher, so // fabricate right and left using top and bottom. TBOX blob_box(sample.geo_feature(GeoBottom), sample.geo_feature(GeoBottom), sample.geo_feature(GeoTop), sample.geo_feature(GeoTop)); // Compute the char_norm_array from the saved cn_feature. FEATURE norm_feature = sample.GetCNFeature(); uinT8* char_norm_array = new uinT8[unicharset.size()]; int num_pruner_classes = MAX(unicharset.size(), PreTrainedTemplates->NumClasses); uinT8* pruner_norm_array = new uinT8[num_pruner_classes]; adapt_results->BlobLength = static_cast(ActualOutlineLength(norm_feature) * 20 + 0.5); ComputeCharNormArrays(norm_feature, PreTrainedTemplates, char_norm_array, pruner_norm_array); PruneClasses(PreTrainedTemplates, num_features, keep_this, sample.features(), pruner_norm_array, shape_table_ != NULL ? &shapetable_cutoffs_[0] : CharNormCutoffs, &adapt_results->CPResults); delete [] pruner_norm_array; if (keep_this >= 0) { adapt_results->CPResults[0].Class = keep_this; adapt_results->CPResults.truncate(1); } if (pruner_only) { // Convert pruner results to output format. for (int i = 0; i < adapt_results->CPResults.size(); ++i) { int class_id = adapt_results->CPResults[i].Class; results->push_back( UnicharRating(class_id, 1.0f - adapt_results->CPResults[i].Rating)); } } else { MasterMatcher(PreTrainedTemplates, num_features, sample.features(), char_norm_array, NULL, matcher_debug_flags, classify_integer_matcher_multiplier, blob_box, adapt_results->CPResults, adapt_results); // Convert master matcher results to output format. for (int i = 0; i < adapt_results->match.size(); i++) { results->push_back(adapt_results->match[i]); } results->sort(&UnicharRating::SortDescendingRating); } delete [] char_norm_array; delete adapt_results; return num_features; } /* CharNormTrainingSample */ /*---------------------------------------------------------------------------*/ /** * This routine computes a rating which reflects the * likelihood that the blob being classified is a noise * blob. NOTE: assumes that the blob length has already been * computed and placed into Results. * * @param results results to add noise classification to * * Globals: * - matcher_avg_noise_size avg. length of a noise blob * * @note Exceptions: none * @note History: Tue Mar 12 18:36:52 1991, DSJ, Created. */ void Classify::ClassifyAsNoise(ADAPT_RESULTS *results) { float rating = results->BlobLength / matcher_avg_noise_size; rating *= rating; rating /= 1.0 + rating; AddNewResult(UnicharRating(UNICHAR_SPACE, 1.0f - rating), results); } /* ClassifyAsNoise */ /// The function converts the given match ratings to the list of blob /// choices with ratings and certainties (used by the context checkers). /// If character fragments are present in the results, this function also makes /// sure that there is at least one non-fragmented classification included. /// For each classification result check the unicharset for "definite" /// ambiguities and modify the resulting Choices accordingly. void Classify::ConvertMatchesToChoices(const DENORM& denorm, const TBOX& box, ADAPT_RESULTS *Results, BLOB_CHOICE_LIST *Choices) { assert(Choices != NULL); FLOAT32 Rating; FLOAT32 Certainty; BLOB_CHOICE_IT temp_it; bool contains_nonfrag = false; temp_it.set_to_list(Choices); int choices_length = 0; // With no shape_table_ maintain the previous MAX_MATCHES as the maximum // number of returned results, but with a shape_table_ we want to have room // for at least the biggest shape (which might contain hundreds of Indic // grapheme fragments) and more, so use double the size of the biggest shape // if that is more than the default. int max_matches = MAX_MATCHES; if (shape_table_ != NULL) { max_matches = shape_table_->MaxNumUnichars() * 2; if (max_matches < MAX_MATCHES) max_matches = MAX_MATCHES; } float best_certainty = -MAX_FLOAT32; for (int i = 0; i < Results->match.size(); i++) { const UnicharRating& result = Results->match[i]; bool adapted = result.adapted; bool current_is_frag = (unicharset.get_fragment(result.unichar_id) != NULL); if (temp_it.length()+1 == max_matches && !contains_nonfrag && current_is_frag) { continue; // look for a non-fragmented character to fill the // last spot in Choices if only fragments are present } // BlobLength can never be legally 0, this means recognition failed. // But we must return a classification result because some invoking // functions (chopper/permuter) do not anticipate a null blob choice. // So we need to assign a poor, but not infinitely bad score. if (Results->BlobLength == 0) { Certainty = -20; Rating = 100; // should be -certainty * real_blob_length } else { Rating = Certainty = (1.0f - result.rating); Rating *= rating_scale * Results->BlobLength; Certainty *= -(getDict().certainty_scale); } // Adapted results, by their very nature, should have good certainty. // Those that don't are at best misleading, and often lead to errors, // so don't accept adapted results that are too far behind the best result, // whether adapted or static. // TODO(rays) find some way of automatically tuning these constants. if (Certainty > best_certainty) { best_certainty = MIN(Certainty, classify_adapted_pruning_threshold); } else if (adapted && Certainty / classify_adapted_pruning_factor < best_certainty) { continue; // Don't accept bad adapted results. } float min_xheight, max_xheight, yshift; denorm.XHeightRange(result.unichar_id, unicharset, box, &min_xheight, &max_xheight, &yshift); BLOB_CHOICE* choice = new BLOB_CHOICE(result.unichar_id, Rating, Certainty, unicharset.get_script(result.unichar_id), min_xheight, max_xheight, yshift, adapted ? BCC_ADAPTED_CLASSIFIER : BCC_STATIC_CLASSIFIER); choice->set_fonts(result.fonts); temp_it.add_to_end(choice); contains_nonfrag |= !current_is_frag; // update contains_nonfrag choices_length++; if (choices_length >= max_matches) break; } Results->match.truncate(choices_length); } // ConvertMatchesToChoices /*---------------------------------------------------------------------------*/ #ifndef GRAPHICS_DISABLED /** * * @param blob blob whose classification is being debugged * @param Results results of match being debugged * * Globals: none * * @note Exceptions: none * @note History: Wed Mar 13 16:44:41 1991, DSJ, Created. */ void Classify::DebugAdaptiveClassifier(TBLOB *blob, ADAPT_RESULTS *Results) { if (static_classifier_ == NULL) return; INT_FX_RESULT_STRUCT fx_info; GenericVector bl_features; TrainingSample* sample = BlobToTrainingSample(*blob, false, &fx_info, &bl_features); if (sample == NULL) return; static_classifier_->DebugDisplay(*sample, blob->denorm().pix(), Results->best_unichar_id); } /* DebugAdaptiveClassifier */ #endif /*---------------------------------------------------------------------------*/ /** * This routine performs an adaptive classification. * If we have not yet adapted to enough classes, a simple * classification to the pre-trained templates is performed. * Otherwise, we match the blob against the adapted templates. * If the adapted templates do not match well, we try a * match against the pre-trained templates. If an adapted * template match is found, we do a match to any pre-trained * templates which could be ambiguous. The results from all * of these classifications are merged together into Results. * * @param Blob blob to be classified * @param Results place to put match results * * Globals: * - PreTrainedTemplates built-in training templates * - AdaptedTemplates templates adapted for this page * - matcher_reliable_adaptive_result rating limit for a great match * * @note Exceptions: none * @note History: Tue Mar 12 08:50:11 1991, DSJ, Created. */ void Classify::DoAdaptiveMatch(TBLOB *Blob, ADAPT_RESULTS *Results) { UNICHAR_ID *Ambiguities; INT_FX_RESULT_STRUCT fx_info; GenericVector bl_features; TrainingSample* sample = BlobToTrainingSample(*Blob, classify_nonlinear_norm, &fx_info, &bl_features); if (sample == NULL) return; if (AdaptedTemplates->NumPermClasses < matcher_permanent_classes_min || tess_cn_matching) { CharNormClassifier(Blob, *sample, Results); } else { Ambiguities = BaselineClassifier(Blob, bl_features, fx_info, AdaptedTemplates, Results); if ((!Results->match.empty() && MarginalMatch(Results->best_rating, matcher_reliable_adaptive_result) && !tess_bn_matching) || Results->match.empty()) { CharNormClassifier(Blob, *sample, Results); } else if (Ambiguities && *Ambiguities >= 0 && !tess_bn_matching) { AmbigClassifier(bl_features, fx_info, Blob, PreTrainedTemplates, AdaptedTemplates->Class, Ambiguities, Results); } } // Force the blob to be classified as noise // if the results contain only fragments. // TODO(daria): verify that this is better than // just adding a NULL classification. if (!Results->HasNonfragment || Results->match.empty()) ClassifyAsNoise(Results); delete sample; } /* DoAdaptiveMatch */ /*---------------------------------------------------------------------------*/ /** * This routine matches blob to the built-in templates * to find out if there are any classes other than the correct * class which are potential ambiguities. * * @param Blob blob to get classification ambiguities for * @param CorrectClass correct class for Blob * * Globals: * - CurrentRatings used by qsort compare routine * - PreTrainedTemplates built-in templates * * @return String containing all possible ambiguous classes. * @note Exceptions: none * @note History: Fri Mar 15 08:08:22 1991, DSJ, Created. */ UNICHAR_ID *Classify::GetAmbiguities(TBLOB *Blob, CLASS_ID CorrectClass) { ADAPT_RESULTS *Results = new ADAPT_RESULTS(); UNICHAR_ID *Ambiguities; int i; Results->Initialize(); INT_FX_RESULT_STRUCT fx_info; GenericVector bl_features; TrainingSample* sample = BlobToTrainingSample(*Blob, classify_nonlinear_norm, &fx_info, &bl_features); if (sample == NULL) { delete Results; return NULL; } CharNormClassifier(Blob, *sample, Results); delete sample; RemoveBadMatches(Results); Results->match.sort(&UnicharRating::SortDescendingRating); /* copy the class id's into an string of ambiguities - don't copy if the correct class is the only class id matched */ Ambiguities = new UNICHAR_ID[Results->match.size() + 1]; if (Results->match.size() > 1 || (Results->match.size() == 1 && Results->match[0].unichar_id != CorrectClass)) { for (i = 0; i < Results->match.size(); i++) Ambiguities[i] = Results->match[i].unichar_id; Ambiguities[i] = -1; } else { Ambiguities[0] = -1; } delete Results; return Ambiguities; } /* GetAmbiguities */ // Returns true if the given blob looks too dissimilar to any character // present in the classifier templates. bool Classify::LooksLikeGarbage(TBLOB *blob) { BLOB_CHOICE_LIST *ratings = new BLOB_CHOICE_LIST(); AdaptiveClassifier(blob, ratings); BLOB_CHOICE_IT ratings_it(ratings); const UNICHARSET &unicharset = getDict().getUnicharset(); if (classify_debug_character_fragments) { print_ratings_list("======================\nLooksLikeGarbage() got ", ratings, unicharset); } for (ratings_it.mark_cycle_pt(); !ratings_it.cycled_list(); ratings_it.forward()) { if (unicharset.get_fragment(ratings_it.data()->unichar_id()) != NULL) { continue; } float certainty = ratings_it.data()->certainty(); delete ratings; return certainty < classify_character_fragments_garbage_certainty_threshold; } delete ratings; return true; // no whole characters in ratings } /*---------------------------------------------------------------------------*/ /** * This routine calls the integer (Hardware) feature * extractor if it has not been called before for this blob. * * The results from the feature extractor are placed into * globals so that they can be used in other routines without * re-extracting the features. * * It then copies the char norm features into the IntFeatures * array provided by the caller. * * @param templates used to compute char norm adjustments * @param pruner_norm_array Array of factors from blob normalization * process * @param char_norm_array array to fill with dummy char norm adjustments * @param fx_info * * Globals: * * @return Number of features extracted or 0 if an error occurred. * @note Exceptions: none * @note History: Tue May 28 10:40:52 1991, DSJ, Created. */ int Classify::GetCharNormFeature(const INT_FX_RESULT_STRUCT& fx_info, INT_TEMPLATES templates, uinT8* pruner_norm_array, uinT8* char_norm_array) { FEATURE norm_feature = NewFeature(&CharNormDesc); float baseline = kBlnBaselineOffset; float scale = MF_SCALE_FACTOR; norm_feature->Params[CharNormY] = (fx_info.Ymean - baseline) * scale; norm_feature->Params[CharNormLength] = fx_info.Length * scale / LENGTH_COMPRESSION; norm_feature->Params[CharNormRx] = fx_info.Rx * scale; norm_feature->Params[CharNormRy] = fx_info.Ry * scale; // Deletes norm_feature. ComputeCharNormArrays(norm_feature, templates, char_norm_array, pruner_norm_array); return IntCastRounded(fx_info.Length / kStandardFeatureLength); } /* GetCharNormFeature */ // Computes the char_norm_array for the unicharset and, if not NULL, the // pruner_array as appropriate according to the existence of the shape_table. void Classify::ComputeCharNormArrays(FEATURE_STRUCT* norm_feature, INT_TEMPLATES_STRUCT* templates, uinT8* char_norm_array, uinT8* pruner_array) { ComputeIntCharNormArray(*norm_feature, char_norm_array); if (pruner_array != NULL) { if (shape_table_ == NULL) { ComputeIntCharNormArray(*norm_feature, pruner_array); } else { memset(pruner_array, MAX_UINT8, templates->NumClasses * sizeof(pruner_array[0])); // Each entry in the pruner norm array is the MIN of all the entries of // the corresponding unichars in the CharNormArray. for (int id = 0; id < templates->NumClasses; ++id) { int font_set_id = templates->Class[id]->font_set_id; const FontSet &fs = fontset_table_.get(font_set_id); for (int config = 0; config < fs.size; ++config) { const Shape& shape = shape_table_->GetShape(fs.configs[config]); for (int c = 0; c < shape.size(); ++c) { if (char_norm_array[shape[c].unichar_id] < pruner_array[id]) pruner_array[id] = char_norm_array[shape[c].unichar_id]; } } } } } FreeFeature(norm_feature); } /*---------------------------------------------------------------------------*/ /** * * @param Templates adapted templates to add new config to * @param ClassId class id to associate with new config * @param FontinfoId font information inferred from pre-trained templates * @param NumFeatures number of features in IntFeatures * @param Features features describing model for new config * @param FloatFeatures floating-pt representation of features * * @return The id of the new config created, a negative integer in * case of error. * @note Exceptions: none * @note History: Fri Mar 15 08:49:46 1991, DSJ, Created. */ int Classify::MakeNewTemporaryConfig(ADAPT_TEMPLATES Templates, CLASS_ID ClassId, int FontinfoId, int NumFeatures, INT_FEATURE_ARRAY Features, FEATURE_SET FloatFeatures) { INT_CLASS IClass; ADAPT_CLASS Class; PROTO_ID OldProtos[MAX_NUM_PROTOS]; FEATURE_ID BadFeatures[MAX_NUM_INT_FEATURES]; int NumOldProtos; int NumBadFeatures; int MaxProtoId, OldMaxProtoId; int BlobLength = 0; int MaskSize; int ConfigId; TEMP_CONFIG Config; int i; int debug_level = NO_DEBUG; if (classify_learning_debug_level >= 3) debug_level = PRINT_MATCH_SUMMARY | PRINT_FEATURE_MATCHES | PRINT_PROTO_MATCHES; IClass = ClassForClassId(Templates->Templates, ClassId); Class = Templates->Class[ClassId]; if (IClass->NumConfigs >= MAX_NUM_CONFIGS) { ++NumAdaptationsFailed; if (classify_learning_debug_level >= 1) cprintf("Cannot make new temporary config: maximum number exceeded.\n"); return -1; } OldMaxProtoId = IClass->NumProtos - 1; NumOldProtos = im_.FindGoodProtos(IClass, AllProtosOn, AllConfigsOff, BlobLength, NumFeatures, Features, OldProtos, classify_adapt_proto_threshold, debug_level); MaskSize = WordsInVectorOfSize(MAX_NUM_PROTOS); zero_all_bits(TempProtoMask, MaskSize); for (i = 0; i < NumOldProtos; i++) SET_BIT(TempProtoMask, OldProtos[i]); NumBadFeatures = im_.FindBadFeatures(IClass, TempProtoMask, AllConfigsOn, BlobLength, NumFeatures, Features, BadFeatures, classify_adapt_feature_threshold, debug_level); MaxProtoId = MakeNewTempProtos(FloatFeatures, NumBadFeatures, BadFeatures, IClass, Class, TempProtoMask); if (MaxProtoId == NO_PROTO) { ++NumAdaptationsFailed; if (classify_learning_debug_level >= 1) cprintf("Cannot make new temp protos: maximum number exceeded.\n"); return -1; } ConfigId = AddIntConfig(IClass); ConvertConfig(TempProtoMask, ConfigId, IClass); Config = NewTempConfig(MaxProtoId, FontinfoId); TempConfigFor(Class, ConfigId) = Config; copy_all_bits(TempProtoMask, Config->Protos, Config->ProtoVectorSize); if (classify_learning_debug_level >= 1) cprintf("Making new temp config %d fontinfo id %d" " using %d old and %d new protos.\n", ConfigId, Config->FontinfoId, NumOldProtos, MaxProtoId - OldMaxProtoId); return ConfigId; } /* MakeNewTemporaryConfig */ /*---------------------------------------------------------------------------*/ /** * This routine finds sets of sequential bad features * that all have the same angle and converts each set into * a new temporary proto. The temp proto is added to the * proto pruner for IClass, pushed onto the list of temp * protos in Class, and added to TempProtoMask. * * @param Features floating-pt features describing new character * @param NumBadFeat number of bad features to turn into protos * @param BadFeat feature id's of bad features * @param IClass integer class templates to add new protos to * @param Class adapted class templates to add new protos to * @param TempProtoMask proto mask to add new protos to * * Globals: none * * @return Max proto id in class after all protos have been added. * Exceptions: none * History: Fri Mar 15 11:39:38 1991, DSJ, Created. */ PROTO_ID Classify::MakeNewTempProtos(FEATURE_SET Features, int NumBadFeat, FEATURE_ID BadFeat[], INT_CLASS IClass, ADAPT_CLASS Class, BIT_VECTOR TempProtoMask) { FEATURE_ID *ProtoStart; FEATURE_ID *ProtoEnd; FEATURE_ID *LastBad; TEMP_PROTO TempProto; PROTO Proto; FEATURE F1, F2; FLOAT32 X1, X2, Y1, Y2; FLOAT32 A1, A2, AngleDelta; FLOAT32 SegmentLength; PROTO_ID Pid; for (ProtoStart = BadFeat, LastBad = ProtoStart + NumBadFeat; ProtoStart < LastBad; ProtoStart = ProtoEnd) { F1 = Features->Features[*ProtoStart]; X1 = F1->Params[PicoFeatX]; Y1 = F1->Params[PicoFeatY]; A1 = F1->Params[PicoFeatDir]; for (ProtoEnd = ProtoStart + 1, SegmentLength = GetPicoFeatureLength(); ProtoEnd < LastBad; ProtoEnd++, SegmentLength += GetPicoFeatureLength()) { F2 = Features->Features[*ProtoEnd]; X2 = F2->Params[PicoFeatX]; Y2 = F2->Params[PicoFeatY]; A2 = F2->Params[PicoFeatDir]; AngleDelta = fabs(A1 - A2); if (AngleDelta > 0.5) AngleDelta = 1.0 - AngleDelta; if (AngleDelta > matcher_clustering_max_angle_delta || fabs(X1 - X2) > SegmentLength || fabs(Y1 - Y2) > SegmentLength) break; } F2 = Features->Features[*(ProtoEnd - 1)]; X2 = F2->Params[PicoFeatX]; Y2 = F2->Params[PicoFeatY]; A2 = F2->Params[PicoFeatDir]; Pid = AddIntProto(IClass); if (Pid == NO_PROTO) return (NO_PROTO); TempProto = NewTempProto(); Proto = &(TempProto->Proto); /* compute proto params - NOTE that Y_DIM_OFFSET must be used because ConvertProto assumes that the Y dimension varies from -0.5 to 0.5 instead of the -0.25 to 0.75 used in baseline normalization */ Proto->Length = SegmentLength; Proto->Angle = A1; Proto->X = (X1 + X2) / 2.0; Proto->Y = (Y1 + Y2) / 2.0 - Y_DIM_OFFSET; FillABC(Proto); TempProto->ProtoId = Pid; SET_BIT(TempProtoMask, Pid); ConvertProto(Proto, Pid, IClass); AddProtoToProtoPruner(Proto, Pid, IClass, classify_learning_debug_level >= 2); Class->TempProtos = push(Class->TempProtos, TempProto); } return IClass->NumProtos - 1; } /* MakeNewTempProtos */ /*---------------------------------------------------------------------------*/ /** * * @param Templates current set of adaptive templates * @param ClassId class containing config to be made permanent * @param ConfigId config to be made permanent * @param Blob current blob being adapted to * * Globals: none * * @note Exceptions: none * @note History: Thu Mar 14 15:54:08 1991, DSJ, Created. */ void Classify::MakePermanent(ADAPT_TEMPLATES Templates, CLASS_ID ClassId, int ConfigId, TBLOB *Blob) { UNICHAR_ID *Ambigs; TEMP_CONFIG Config; ADAPT_CLASS Class; PROTO_KEY ProtoKey; Class = Templates->Class[ClassId]; Config = TempConfigFor(Class, ConfigId); MakeConfigPermanent(Class, ConfigId); if (Class->NumPermConfigs == 0) Templates->NumPermClasses++; Class->NumPermConfigs++; // Initialize permanent config. Ambigs = GetAmbiguities(Blob, ClassId); PERM_CONFIG Perm = (PERM_CONFIG) alloc_struct(sizeof(PERM_CONFIG_STRUCT), "PERM_CONFIG_STRUCT"); Perm->Ambigs = Ambigs; Perm->FontinfoId = Config->FontinfoId; // Free memory associated with temporary config (since ADAPTED_CONFIG // is a union we need to clean up before we record permanent config). ProtoKey.Templates = Templates; ProtoKey.ClassId = ClassId; ProtoKey.ConfigId = ConfigId; Class->TempProtos = delete_d(Class->TempProtos, &ProtoKey, MakeTempProtoPerm); FreeTempConfig(Config); // Record permanent config. PermConfigFor(Class, ConfigId) = Perm; if (classify_learning_debug_level >= 1) { tprintf("Making config %d for %s (ClassId %d) permanent:" " fontinfo id %d, ambiguities '", ConfigId, getDict().getUnicharset().debug_str(ClassId).string(), ClassId, PermConfigFor(Class, ConfigId)->FontinfoId); for (UNICHAR_ID *AmbigsPointer = Ambigs; *AmbigsPointer >= 0; ++AmbigsPointer) tprintf("%s", unicharset.id_to_unichar(*AmbigsPointer)); tprintf("'.\n"); } } /* MakePermanent */ } // namespace tesseract /*---------------------------------------------------------------------------*/ /** * This routine converts TempProto to be permanent if * its proto id is used by the configuration specified in * ProtoKey. * * @param item1 (TEMP_PROTO) temporary proto to compare to key * @param item2 (PROTO_KEY) defines which protos to make permanent * * Globals: none * * @return TRUE if TempProto is converted, FALSE otherwise * @note Exceptions: none * @note History: Thu Mar 14 18:49:54 1991, DSJ, Created. */ int MakeTempProtoPerm(void *item1, void *item2) { ADAPT_CLASS Class; TEMP_CONFIG Config; TEMP_PROTO TempProto; PROTO_KEY *ProtoKey; TempProto = (TEMP_PROTO) item1; ProtoKey = (PROTO_KEY *) item2; Class = ProtoKey->Templates->Class[ProtoKey->ClassId]; Config = TempConfigFor(Class, ProtoKey->ConfigId); if (TempProto->ProtoId > Config->MaxProtoId || !test_bit (Config->Protos, TempProto->ProtoId)) return FALSE; MakeProtoPermanent(Class, TempProto->ProtoId); AddProtoToClassPruner(&(TempProto->Proto), ProtoKey->ClassId, ProtoKey->Templates->Templates); FreeTempProto(TempProto); return TRUE; } /* MakeTempProtoPerm */ /*---------------------------------------------------------------------------*/ namespace tesseract { /** * This routine writes the matches in Results to File. * * @param results match results to write to File * * Globals: none * * @note Exceptions: none * @note History: Mon Mar 18 09:24:53 1991, DSJ, Created. */ void Classify::PrintAdaptiveMatchResults(const ADAPT_RESULTS& results) { for (int i = 0; i < results.match.size(); ++i) { tprintf("%s ", unicharset.debug_str(results.match[i].unichar_id).string()); results.match[i].Print(); } } /* PrintAdaptiveMatchResults */ /*---------------------------------------------------------------------------*/ /** * This routine steps through each matching class in Results * and removes it from the match list if its rating * is worse than the BestRating plus a pad. In other words, * all good matches get moved to the front of the classes * array. * * @param Results contains matches to be filtered * * Globals: * - matcher_bad_match_pad defines a "bad match" * * @note Exceptions: none * @note History: Tue Mar 12 13:51:03 1991, DSJ, Created. */ void Classify::RemoveBadMatches(ADAPT_RESULTS *Results) { int Next, NextGood; FLOAT32 BadMatchThreshold; static const char* romans = "i v x I V X"; BadMatchThreshold = Results->best_rating - matcher_bad_match_pad; if (classify_bln_numeric_mode) { UNICHAR_ID unichar_id_one = unicharset.contains_unichar("1") ? unicharset.unichar_to_id("1") : -1; UNICHAR_ID unichar_id_zero = unicharset.contains_unichar("0") ? unicharset.unichar_to_id("0") : -1; float scored_one = ScoredUnichar(unichar_id_one, *Results); float scored_zero = ScoredUnichar(unichar_id_zero, *Results); for (Next = NextGood = 0; Next < Results->match.size(); Next++) { const UnicharRating& match = Results->match[Next]; if (match.rating >= BadMatchThreshold) { if (!unicharset.get_isalpha(match.unichar_id) || strstr(romans, unicharset.id_to_unichar(match.unichar_id)) != NULL) { } else if (unicharset.eq(match.unichar_id, "l") && scored_one < BadMatchThreshold) { Results->match[Next].unichar_id = unichar_id_one; } else if (unicharset.eq(match.unichar_id, "O") && scored_zero < BadMatchThreshold) { Results->match[Next].unichar_id = unichar_id_zero; } else { Results->match[Next].unichar_id = INVALID_UNICHAR_ID; // Don't copy. } if (Results->match[Next].unichar_id != INVALID_UNICHAR_ID) { if (NextGood == Next) { ++NextGood; } else { Results->match[NextGood++] = Results->match[Next]; } } } } } else { for (Next = NextGood = 0; Next < Results->match.size(); Next++) { if (Results->match[Next].rating >= BadMatchThreshold) { if (NextGood == Next) { ++NextGood; } else { Results->match[NextGood++] = Results->match[Next]; } } } } Results->match.truncate(NextGood); } /* RemoveBadMatches */ /*----------------------------------------------------------------------------*/ /** * This routine discards extra digits or punctuation from the results. * We keep only the top 2 punctuation answers and the top 1 digit answer if * present. * * @param Results contains matches to be filtered * * @note History: Tue Mar 12 13:51:03 1991, DSJ, Created. */ void Classify::RemoveExtraPuncs(ADAPT_RESULTS *Results) { int Next, NextGood; int punc_count; /*no of garbage characters */ int digit_count; /*garbage characters */ static char punc_chars[] = ". , ; : / ` ~ ' - = \\ | \" ! _ ^"; static char digit_chars[] = "0 1 2 3 4 5 6 7 8 9"; punc_count = 0; digit_count = 0; for (Next = NextGood = 0; Next < Results->match.size(); Next++) { const UnicharRating& match = Results->match[Next]; bool keep = true; if (strstr(punc_chars, unicharset.id_to_unichar(match.unichar_id)) != NULL) { if (punc_count >= 2) keep = false; punc_count++; } else { if (strstr(digit_chars, unicharset.id_to_unichar(match.unichar_id)) != NULL) { if (digit_count >= 1) keep = false; digit_count++; } } if (keep) { if (NextGood == Next) { ++NextGood; } else { Results->match[NextGood++] = match; } } } Results->match.truncate(NextGood); } /* RemoveExtraPuncs */ /*---------------------------------------------------------------------------*/ /** * This routine resets the internal thresholds inside * the integer matcher to correspond to the specified * threshold. * * @param Threshold threshold for creating new templates * * Globals: * - matcher_good_threshold default good match rating * * @note Exceptions: none * @note History: Tue Apr 9 08:33:13 1991, DSJ, Created. */ void Classify::SetAdaptiveThreshold(FLOAT32 Threshold) { Threshold = (Threshold == matcher_good_threshold) ? 0.9: (1.0 - Threshold); classify_adapt_proto_threshold.set_value( ClipToRange(255 * Threshold, 0, 255)); classify_adapt_feature_threshold.set_value( ClipToRange(255 * Threshold, 0, 255)); } /* SetAdaptiveThreshold */ /*---------------------------------------------------------------------------*/ /** * This routine displays debug information for the best config * of the given shape_id for the given set of features. * * @param shape_id classifier id to work with * @param features features of the unknown character * @param num_features Number of features in the features array. * * @note Exceptions: none * @note History: Fri Mar 22 08:43:52 1991, DSJ, Created. */ void Classify::ShowBestMatchFor(int shape_id, const INT_FEATURE_STRUCT* features, int num_features) { #ifndef GRAPHICS_DISABLED uinT32 config_mask; if (UnusedClassIdIn(PreTrainedTemplates, shape_id)) { tprintf("No built-in templates for class/shape %d\n", shape_id); return; } if (num_features <= 0) { tprintf("Illegal blob (char norm features)!\n"); return; } UnicharRating cn_result; classify_norm_method.set_value(character); im_.Match(ClassForClassId(PreTrainedTemplates, shape_id), AllProtosOn, AllConfigsOn, num_features, features, &cn_result, classify_adapt_feature_threshold, NO_DEBUG, matcher_debug_separate_windows); tprintf("\n"); config_mask = 1 << cn_result.config; tprintf("Static Shape ID: %d\n", shape_id); ShowMatchDisplay(); im_.Match(ClassForClassId(PreTrainedTemplates, shape_id), AllProtosOn, reinterpret_cast(&config_mask), num_features, features, &cn_result, classify_adapt_feature_threshold, matcher_debug_flags, matcher_debug_separate_windows); UpdateMatchDisplay(); #endif // GRAPHICS_DISABLED } /* ShowBestMatchFor */ // Returns a string for the classifier class_id: either the corresponding // unicharset debug_str or the shape_table_ debug str. STRING Classify::ClassIDToDebugStr(const INT_TEMPLATES_STRUCT* templates, int class_id, int config_id) const { STRING class_string; if (templates == PreTrainedTemplates && shape_table_ != NULL) { int shape_id = ClassAndConfigIDToFontOrShapeID(class_id, config_id); class_string = shape_table_->DebugStr(shape_id); } else { class_string = unicharset.debug_str(class_id); } return class_string; } // Converts a classifier class_id index to a shape_table_ index int Classify::ClassAndConfigIDToFontOrShapeID(int class_id, int int_result_config) const { int font_set_id = PreTrainedTemplates->Class[class_id]->font_set_id; // Older inttemps have no font_ids. if (font_set_id < 0) return kBlankFontinfoId; const FontSet &fs = fontset_table_.get(font_set_id); ASSERT_HOST(int_result_config >= 0 && int_result_config < fs.size); return fs.configs[int_result_config]; } // Converts a shape_table_ index to a classifier class_id index (not a // unichar-id!). Uses a search, so not fast. int Classify::ShapeIDToClassID(int shape_id) const { for (int id = 0; id < PreTrainedTemplates->NumClasses; ++id) { int font_set_id = PreTrainedTemplates->Class[id]->font_set_id; ASSERT_HOST(font_set_id >= 0); const FontSet &fs = fontset_table_.get(font_set_id); for (int config = 0; config < fs.size; ++config) { if (fs.configs[config] == shape_id) return id; } } tprintf("Shape %d not found\n", shape_id); return -1; } // Returns true if the given TEMP_CONFIG is good enough to make it // a permanent config. bool Classify::TempConfigReliable(CLASS_ID class_id, const TEMP_CONFIG &config) { if (classify_learning_debug_level >= 1) { tprintf("NumTimesSeen for config of %s is %d\n", getDict().getUnicharset().debug_str(class_id).string(), config->NumTimesSeen); } if (config->NumTimesSeen >= matcher_sufficient_examples_for_prototyping) { return true; } else if (config->NumTimesSeen < matcher_min_examples_for_prototyping) { return false; } else if (use_ambigs_for_adaption) { // Go through the ambigs vector and see whether we have already seen // enough times all the characters represented by the ambigs vector. const UnicharIdVector *ambigs = getDict().getUnicharAmbigs().AmbigsForAdaption(class_id); int ambigs_size = (ambigs == NULL) ? 0 : ambigs->size(); for (int ambig = 0; ambig < ambigs_size; ++ambig) { ADAPT_CLASS ambig_class = AdaptedTemplates->Class[(*ambigs)[ambig]]; assert(ambig_class != NULL); if (ambig_class->NumPermConfigs == 0 && ambig_class->MaxNumTimesSeen < matcher_min_examples_for_prototyping) { if (classify_learning_debug_level >= 1) { tprintf("Ambig %s has not been seen enough times," " not making config for %s permanent\n", getDict().getUnicharset().debug_str( (*ambigs)[ambig]).string(), getDict().getUnicharset().debug_str(class_id).string()); } return false; } } } return true; } void Classify::UpdateAmbigsGroup(CLASS_ID class_id, TBLOB *Blob) { const UnicharIdVector *ambigs = getDict().getUnicharAmbigs().ReverseAmbigsForAdaption(class_id); int ambigs_size = (ambigs == NULL) ? 0 : ambigs->size(); if (classify_learning_debug_level >= 1) { tprintf("Running UpdateAmbigsGroup for %s class_id=%d\n", getDict().getUnicharset().debug_str(class_id).string(), class_id); } for (int ambig = 0; ambig < ambigs_size; ++ambig) { CLASS_ID ambig_class_id = (*ambigs)[ambig]; const ADAPT_CLASS ambigs_class = AdaptedTemplates->Class[ambig_class_id]; for (int cfg = 0; cfg < MAX_NUM_CONFIGS; ++cfg) { if (ConfigIsPermanent(ambigs_class, cfg)) continue; const TEMP_CONFIG config = TempConfigFor(AdaptedTemplates->Class[ambig_class_id], cfg); if (config != NULL && TempConfigReliable(ambig_class_id, config)) { if (classify_learning_debug_level >= 1) { tprintf("Making config %d of %s permanent\n", cfg, getDict().getUnicharset().debug_str( ambig_class_id).string()); } MakePermanent(AdaptedTemplates, ambig_class_id, cfg, Blob); } } } } } // namespace tesseract tesseract-3.04.01/classify/blobclass.cpp000066400000000000000000000110501266071204500201410ustar00rootroot00000000000000/****************************************************************************** ** Filename: blobclass.c ** Purpose: High level blob classification and training routines. ** Author: Dan Johnson ** History: 7/21/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 "blobclass.h" #include #include "classify.h" #include "efio.h" #include "featdefs.h" #include "mf.h" #include "normfeat.h" static const char kUnknownFontName[] = "UnknownFont"; STRING_VAR(classify_font_name, kUnknownFontName, "Default font name to be used in training"); namespace tesseract { /**---------------------------------------------------------------------------- Public Code ----------------------------------------------------------------------------**/ // Finds the name of the training font and returns it in fontname, by cutting // it out based on the expectation that the filename is of the form: // /path/to/dir/[lang].[fontname].exp[num] // The [lang], [fontname] and [num] fields should not have '.' characters. // If the global parameter classify_font_name is set, its value is used instead. void ExtractFontName(const STRING& filename, STRING* fontname) { *fontname = classify_font_name; if (*fontname == kUnknownFontName) { // filename is expected to be of the form [lang].[fontname].exp[num] // The [lang], [fontname] and [num] fields should not have '.' characters. const char *basename = strrchr(filename.string(), '/'); const char *firstdot = strchr(basename ? basename : filename.string(), '.'); const char *lastdot = strrchr(filename.string(), '.'); if (firstdot != lastdot && firstdot != NULL && lastdot != NULL) { ++firstdot; *fontname = firstdot; fontname->truncate_at(lastdot - firstdot); } } } /*---------------------------------------------------------------------------*/ // Extracts features from the given blob and saves them in the tr_file_data_ // member variable. // fontname: Name of font that this blob was printed in. // cn_denorm: Character normalization transformation to apply to the blob. // fx_info: Character normalization parameters computed with cn_denorm. // blob_text: Ground truth text for the blob. void Classify::LearnBlob(const STRING& fontname, TBLOB* blob, const DENORM& cn_denorm, const INT_FX_RESULT_STRUCT& fx_info, const char* blob_text) { CHAR_DESC CharDesc = NewCharDescription(feature_defs_); CharDesc->FeatureSets[0] = ExtractMicros(blob, cn_denorm); CharDesc->FeatureSets[1] = ExtractCharNormFeatures(fx_info); CharDesc->FeatureSets[2] = ExtractIntCNFeatures(*blob, fx_info); CharDesc->FeatureSets[3] = ExtractIntGeoFeatures(*blob, fx_info); if (ValidCharDescription(feature_defs_, CharDesc)) { // Label the features with a class name and font name. tr_file_data_ += "\n"; tr_file_data_ += fontname; tr_file_data_ += " "; tr_file_data_ += blob_text; tr_file_data_ += "\n"; // write micro-features to file and clean up WriteCharDescription(feature_defs_, CharDesc, &tr_file_data_); } else { tprintf("Blob learned was invalid!\n"); } FreeCharDescription(CharDesc); } // LearnBlob // Writes stored training data to a .tr file based on the given filename. // Returns false on error. bool Classify::WriteTRFile(const STRING& filename) { STRING tr_filename = filename + ".tr"; FILE* fp = Efopen(tr_filename.string(), "wb"); int len = tr_file_data_.length(); bool result = fwrite(&tr_file_data_[0], sizeof(tr_file_data_[0]), len, fp) == len; fclose(fp); tr_file_data_.truncate_at(0); return result; } } // namespace tesseract. tesseract-3.04.01/classify/blobclass.h000066400000000000000000000045331266071204500176160ustar00rootroot00000000000000/****************************************************************************** ** Filename: blobclass.h ** Purpose: Interface to high level classification and training. ** Author: Dan Johnson ** History: 5/29/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 BLOBCLASS_H #define BLOBCLASS_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "strngs.h" /*--------------------------------------------------------------------------- Macros ----------------------------------------------------------------------------*/ /* macros for controlling the display of recognized characters */ #define EnableCharDisplay() (DisplayCharacters = TRUE) #define DisableCharDisplay() (DisplayCharacters = FALSE) /* macros for controlling the display of the entire match list */ #define EnableMatchDisplay() (DisplayMatchList = TRUE) #define DisableMatchDisplay() (DisplayMatchList = FALSE) /**---------------------------------------------------------------------------- Public Function Prototypes ----------------------------------------------------------------------------**/ namespace tesseract { // Finds the name of the training font and returns it in fontname, by cutting // it out based on the expectation that the filename is of the form: // /path/to/dir/[lang].[fontname].exp[num] // The [lang], [fontname] and [num] fields should not have '.' characters. // If the global parameter classify_font_name is set, its value is used instead. void ExtractFontName(const STRING& filename, STRING* fontname); } // namespace tesseract. #endif tesseract-3.04.01/classify/classify.cpp000066400000000000000000000261721266071204500200250ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: classify.cpp // Description: classify class. // Author: Samuel Charron // // (C) Copyright 2006, 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 automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "classify.h" #include "fontinfo.h" #include "intproto.h" #include "mfoutline.h" #include "scrollview.h" #include "shapeclassifier.h" #include "shapetable.h" #include "unicity_table.h" #include namespace tesseract { Classify::Classify() : BOOL_MEMBER(allow_blob_division, true, "Use divisible blobs chopping", this->params()), BOOL_MEMBER(prioritize_division, FALSE, "Prioritize blob division over chopping", this->params()), INT_MEMBER(tessedit_single_match, FALSE, "Top choice only from CP", this->params()), BOOL_MEMBER(classify_enable_learning, true, "Enable adaptive classifier", this->params()), INT_MEMBER(classify_debug_level, 0, "Classify debug level", this->params()), INT_MEMBER(classify_norm_method, character, "Normalization Method ...", this->params()), double_MEMBER(classify_char_norm_range, 0.2, "Character Normalization Range ...", this->params()), double_MEMBER(classify_min_norm_scale_x, 0.0, "Min char x-norm scale ...", this->params()), /* PREV DEFAULT 0.1 */ double_MEMBER(classify_max_norm_scale_x, 0.325, "Max char x-norm scale ...", this->params()), /* PREV DEFAULT 0.3 */ double_MEMBER(classify_min_norm_scale_y, 0.0, "Min char y-norm scale ...", this->params()), /* PREV DEFAULT 0.1 */ double_MEMBER(classify_max_norm_scale_y, 0.325, "Max char y-norm scale ...", this->params()), /* PREV DEFAULT 0.3 */ double_MEMBER(classify_max_rating_ratio, 1.5, "Veto ratio between classifier ratings", this->params()), double_MEMBER(classify_max_certainty_margin, 5.5, "Veto difference between classifier certainties", this->params()), BOOL_MEMBER(tess_cn_matching, 0, "Character Normalized Matching", this->params()), BOOL_MEMBER(tess_bn_matching, 0, "Baseline Normalized Matching", this->params()), BOOL_MEMBER(classify_enable_adaptive_matcher, 1, "Enable adaptive classifier", this->params()), BOOL_MEMBER(classify_use_pre_adapted_templates, 0, "Use pre-adapted classifier templates", this->params()), BOOL_MEMBER(classify_save_adapted_templates, 0, "Save adapted templates to a file", this->params()), BOOL_MEMBER(classify_enable_adaptive_debugger, 0, "Enable match debugger", this->params()), BOOL_MEMBER(classify_nonlinear_norm, 0, "Non-linear stroke-density normalization", this->params()), INT_MEMBER(matcher_debug_level, 0, "Matcher Debug Level", this->params()), INT_MEMBER(matcher_debug_flags, 0, "Matcher Debug Flags", this->params()), INT_MEMBER(classify_learning_debug_level, 0, "Learning Debug Level: ", this->params()), double_MEMBER(matcher_good_threshold, 0.125, "Good Match (0-1)", this->params()), double_MEMBER(matcher_reliable_adaptive_result, 0.0, "Great Match (0-1)", this->params()), double_MEMBER(matcher_perfect_threshold, 0.02, "Perfect Match (0-1)", this->params()), double_MEMBER(matcher_bad_match_pad, 0.15, "Bad Match Pad (0-1)", this->params()), double_MEMBER(matcher_rating_margin, 0.1, "New template margin (0-1)", this->params()), double_MEMBER(matcher_avg_noise_size, 12.0, "Avg. noise blob length", this->params()), INT_MEMBER(matcher_permanent_classes_min, 1, "Min # of permanent classes", this->params()), INT_MEMBER(matcher_min_examples_for_prototyping, 3, "Reliable Config Threshold", this->params()), INT_MEMBER(matcher_sufficient_examples_for_prototyping, 5, "Enable adaption even if the ambiguities have not been seen", this->params()), double_MEMBER(matcher_clustering_max_angle_delta, 0.015, "Maximum angle delta for prototype clustering", this->params()), double_MEMBER(classify_misfit_junk_penalty, 0.0, "Penalty to apply when a non-alnum is vertically out of " "its expected textline position", this->params()), double_MEMBER(rating_scale, 1.5, "Rating scaling factor", this->params()), double_MEMBER(certainty_scale, 20.0, "Certainty scaling factor", this->params()), double_MEMBER(tessedit_class_miss_scale, 0.00390625, "Scale factor for features not used", this->params()), double_MEMBER( classify_adapted_pruning_factor, 2.5, "Prune poor adapted results this much worse than best result", this->params()), double_MEMBER(classify_adapted_pruning_threshold, -1.0, "Threshold at which classify_adapted_pruning_factor starts", this->params()), INT_MEMBER(classify_adapt_proto_threshold, 230, "Threshold for good protos during adaptive 0-255", this->params()), INT_MEMBER(classify_adapt_feature_threshold, 230, "Threshold for good features during adaptive 0-255", this->params()), BOOL_MEMBER(disable_character_fragments, TRUE, "Do not include character fragments in the" " results of the classifier", this->params()), double_MEMBER(classify_character_fragments_garbage_certainty_threshold, -3.0, "Exclude fragments that do not look like whole" " characters from training and adaption", this->params()), BOOL_MEMBER(classify_debug_character_fragments, FALSE, "Bring up graphical debugging windows for fragments training", this->params()), BOOL_MEMBER(matcher_debug_separate_windows, FALSE, "Use two different windows for debugging the matching: " "One for the protos and one for the features.", this->params()), STRING_MEMBER(classify_learn_debug_str, "", "Class str to debug learning", this->params()), INT_MEMBER(classify_class_pruner_threshold, 229, "Class Pruner Threshold 0-255", this->params()), INT_MEMBER(classify_class_pruner_multiplier, 15, "Class Pruner Multiplier 0-255: ", this->params()), INT_MEMBER(classify_cp_cutoff_strength, 7, "Class Pruner CutoffStrength: ", this->params()), INT_MEMBER(classify_integer_matcher_multiplier, 10, "Integer Matcher Multiplier 0-255: ", this->params()), EnableLearning(true), INT_MEMBER(il1_adaption_test, 0, "Don't adapt to i/I at beginning of word", this->params()), BOOL_MEMBER(classify_bln_numeric_mode, 0, "Assume the input is numbers [0-9].", this->params()), double_MEMBER(speckle_large_max_size, 0.30, "Max large speckle size", this->params()), double_MEMBER(speckle_rating_penalty, 10.0, "Penalty to add to worst rating for noise", this->params()), shape_table_(NULL), dict_(this), static_classifier_(NULL) { fontinfo_table_.set_compare_callback( NewPermanentTessCallback(CompareFontInfo)); fontinfo_table_.set_clear_callback( NewPermanentTessCallback(FontInfoDeleteCallback)); fontset_table_.set_compare_callback( NewPermanentTessCallback(CompareFontSet)); fontset_table_.set_clear_callback( NewPermanentTessCallback(FontSetDeleteCallback)); AdaptedTemplates = NULL; BackupAdaptedTemplates = NULL; PreTrainedTemplates = NULL; AllProtosOn = NULL; AllConfigsOn = NULL; AllConfigsOff = NULL; TempProtoMask = NULL; NormProtos = NULL; NumAdaptationsFailed = 0; learn_debug_win_ = NULL; learn_fragmented_word_debug_win_ = NULL; learn_fragments_debug_win_ = NULL; CharNormCutoffs = new uinT16[MAX_NUM_CLASSES]; BaselineCutoffs = new uinT16[MAX_NUM_CLASSES]; } Classify::~Classify() { EndAdaptiveClassifier(); delete learn_debug_win_; delete learn_fragmented_word_debug_win_; delete learn_fragments_debug_win_; delete[] CharNormCutoffs; delete[] BaselineCutoffs; } // Takes ownership of the given classifier, and uses it for future calls // to CharNormClassifier. void Classify::SetStaticClassifier(ShapeClassifier* static_classifier) { delete static_classifier_; static_classifier_ = static_classifier; } // Moved from speckle.cpp // Adds a noise classification result that is a bit worse than the worst // current result, or the worst possible result if no current results. void Classify::AddLargeSpeckleTo(int blob_length, BLOB_CHOICE_LIST *choices) { BLOB_CHOICE_IT bc_it(choices); // If there is no classifier result, we will use the worst possible certainty // and corresponding rating. float certainty = -getDict().certainty_scale; float rating = rating_scale * blob_length; if (!choices->empty() && blob_length > 0) { bc_it.move_to_last(); BLOB_CHOICE* worst_choice = bc_it.data(); // Add speckle_rating_penalty to worst rating, matching old value. rating = worst_choice->rating() + speckle_rating_penalty; // Compute the rating to correspond to the certainty. (Used to be kept // the same, but that messes up the language model search.) certainty = -rating * getDict().certainty_scale / (rating_scale * blob_length); } BLOB_CHOICE* blob_choice = new BLOB_CHOICE(UNICHAR_SPACE, rating, certainty, -1, 0.0f, MAX_FLOAT32, 0, BCC_SPECKLE_CLASSIFIER); bc_it.add_to_end(blob_choice); } // Returns true if the blob is small enough to be a large speckle. bool Classify::LargeSpeckle(const TBLOB &blob) { double speckle_size = kBlnXHeight * speckle_large_max_size; TBOX bbox = blob.bounding_box(); return bbox.width() < speckle_size && bbox.height() < speckle_size; } } // namespace tesseract tesseract-3.04.01/classify/classify.h000066400000000000000000000654051266071204500174740ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: classify.h // Description: classify class. // Author: Samuel Charron // // (C) Copyright 2006, 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_CLASSIFY_CLASSIFY_H__ #define TESSERACT_CLASSIFY_CLASSIFY_H__ #include "adaptive.h" #include "ccstruct.h" #include "classify.h" #include "dict.h" #include "featdefs.h" #include "fontinfo.h" #include "imagedata.h" #include "intfx.h" #include "intmatcher.h" #include "normalis.h" #include "ratngs.h" #include "ocrfeatures.h" #include "unicity_table.h" class ScrollView; class WERD_CHOICE; class WERD_RES; struct ADAPT_RESULTS; struct NORM_PROTOS; static const int kUnknownFontinfoId = -1; static const int kBlankFontinfoId = -2; namespace tesseract { class ShapeClassifier; struct ShapeRating; class ShapeTable; struct UnicharRating; // How segmented is a blob. In this enum, character refers to a classifiable // unit, but that is too long and character is usually easier to understand. enum CharSegmentationType { CST_FRAGMENT, // A partial character. CST_WHOLE, // A correctly segmented character. CST_IMPROPER, // More than one but less than 2 characters. CST_NGRAM // Multiple characters. }; class Classify : public CCStruct { public: Classify(); virtual ~Classify(); Dict& getDict() { return dict_; } const ShapeTable* shape_table() const { return shape_table_; } // Takes ownership of the given classifier, and uses it for future calls // to CharNormClassifier. void SetStaticClassifier(ShapeClassifier* static_classifier); // Adds a noise classification result that is a bit worse than the worst // current result, or the worst possible result if no current results. void AddLargeSpeckleTo(int blob_length, BLOB_CHOICE_LIST *choices); // Returns true if the blob is small enough to be a large speckle. bool LargeSpeckle(const TBLOB &blob); /* adaptive.cpp ************************************************************/ ADAPT_TEMPLATES NewAdaptedTemplates(bool InitFromUnicharset); int GetFontinfoId(ADAPT_CLASS Class, uinT8 ConfigId); // Runs the class pruner from int_templates on the given features, returning // the number of classes output in results. // int_templates Class pruner tables // num_features Number of features in blob // features Array of features // normalization_factors (input) Array of int_templates->NumClasses fudge // factors from blob normalization process. // (Indexed by CLASS_INDEX) // expected_num_features (input) Array of int_templates->NumClasses // expected number of features for each class. // (Indexed by CLASS_INDEX) // results (output) Sorted Array of pruned classes. // Array must be sized to take the maximum possible // number of outputs : int_templates->NumClasses. int PruneClasses(const INT_TEMPLATES_STRUCT* int_templates, int num_features, int keep_this, const INT_FEATURE_STRUCT* features, const uinT8* normalization_factors, const uinT16* expected_num_features, GenericVector* results); void ReadNewCutoffs(FILE *CutoffFile, bool swap, inT64 end_offset, CLASS_CUTOFF_ARRAY Cutoffs); void PrintAdaptedTemplates(FILE *File, ADAPT_TEMPLATES Templates); void WriteAdaptedTemplates(FILE *File, ADAPT_TEMPLATES Templates); ADAPT_TEMPLATES ReadAdaptedTemplates(FILE *File); /* normmatch.cpp ************************************************************/ FLOAT32 ComputeNormMatch(CLASS_ID ClassId, const FEATURE_STRUCT& feature, BOOL8 DebugMatch); void FreeNormProtos(); NORM_PROTOS *ReadNormProtos(FILE *File, inT64 end_offset); /* protos.cpp ***************************************************************/ void ConvertProto(PROTO Proto, int ProtoId, INT_CLASS Class); INT_TEMPLATES CreateIntTemplates(CLASSES FloatProtos, const UNICHARSET& target_unicharset); /* adaptmatch.cpp ***********************************************************/ // Learns the given word using its chopped_word, seam_array, denorm, // box_word, best_state, and correct_text to learn both correctly and // incorrectly segmented blobs. If fontname is not NULL, then LearnBlob // is called and the data will be saved in an internal buffer. // Otherwise AdaptToBlob is called for adaption within a document. void LearnWord(const char* fontname, WERD_RES* word); // Builds a blob of length fragments, from the word, starting at start, // and then learns it, as having the given correct_text. // If fontname is not NULL, then LearnBlob is called and the data will be // saved in an internal buffer for static training. // Otherwise AdaptToBlob is called for adaption within a document. // threshold is a magic number required by AdaptToChar and generated by // ComputeAdaptionThresholds. // Although it can be partly inferred from the string, segmentation is // provided to explicitly clarify the character segmentation. void LearnPieces(const char* fontname, int start, int length, float threshold, CharSegmentationType segmentation, const char* correct_text, WERD_RES* word); void InitAdaptiveClassifier(bool load_pre_trained_templates); void InitAdaptedClass(TBLOB *Blob, CLASS_ID ClassId, int FontinfoId, ADAPT_CLASS Class, ADAPT_TEMPLATES Templates); void AmbigClassifier(const GenericVector& int_features, const INT_FX_RESULT_STRUCT& fx_info, const TBLOB *blob, INT_TEMPLATES templates, ADAPT_CLASS *classes, UNICHAR_ID *ambiguities, ADAPT_RESULTS *results); void MasterMatcher(INT_TEMPLATES templates, inT16 num_features, const INT_FEATURE_STRUCT* features, const uinT8* norm_factors, ADAPT_CLASS* classes, int debug, int matcher_multiplier, const TBOX& blob_box, const GenericVector& results, ADAPT_RESULTS* final_results); // Converts configs to fonts, and if the result is not adapted, and a // shape_table_ is present, the shape is expanded to include all // unichar_ids represented, before applying a set of corrections to the // distance rating in int_result, (see ComputeCorrectedRating.) // The results are added to the final_results output. void ExpandShapesAndApplyCorrections(ADAPT_CLASS* classes, bool debug, int class_id, int bottom, int top, float cp_rating, int blob_length, int matcher_multiplier, const uinT8* cn_factors, UnicharRating* int_result, ADAPT_RESULTS* final_results); // Applies a set of corrections to the distance im_rating, // including the cn_correction, miss penalty and additional penalty // for non-alnums being vertical misfits. Returns the corrected distance. double ComputeCorrectedRating(bool debug, int unichar_id, double cp_rating, double im_rating, int feature_misses, int bottom, int top, int blob_length, int matcher_multiplier, const uinT8* cn_factors); void ConvertMatchesToChoices(const DENORM& denorm, const TBOX& box, ADAPT_RESULTS *Results, BLOB_CHOICE_LIST *Choices); void AddNewResult(const UnicharRating& new_result, ADAPT_RESULTS *results); int GetAdaptiveFeatures(TBLOB *Blob, INT_FEATURE_ARRAY IntFeatures, FEATURE_SET *FloatFeatures); #ifndef GRAPHICS_DISABLED void DebugAdaptiveClassifier(TBLOB *Blob, ADAPT_RESULTS *Results); #endif PROTO_ID MakeNewTempProtos(FEATURE_SET Features, int NumBadFeat, FEATURE_ID BadFeat[], INT_CLASS IClass, ADAPT_CLASS Class, BIT_VECTOR TempProtoMask); int MakeNewTemporaryConfig(ADAPT_TEMPLATES Templates, CLASS_ID ClassId, int FontinfoId, int NumFeatures, INT_FEATURE_ARRAY Features, FEATURE_SET FloatFeatures); void MakePermanent(ADAPT_TEMPLATES Templates, CLASS_ID ClassId, int ConfigId, TBLOB *Blob); void PrintAdaptiveMatchResults(const ADAPT_RESULTS& results); void RemoveExtraPuncs(ADAPT_RESULTS *Results); void RemoveBadMatches(ADAPT_RESULTS *Results); void SetAdaptiveThreshold(FLOAT32 Threshold); void ShowBestMatchFor(int shape_id, const INT_FEATURE_STRUCT* features, int num_features); // Returns a string for the classifier class_id: either the corresponding // unicharset debug_str or the shape_table_ debug str. STRING ClassIDToDebugStr(const INT_TEMPLATES_STRUCT* templates, int class_id, int config_id) const; // Converts a classifier class_id index with a config ID to: // shape_table_ present: a shape_table_ index OR // No shape_table_: a font ID. // Without shape training, each class_id, config pair represents a single // unichar id/font combination, so this function looks up the corresponding // font id. // With shape training, each class_id, config pair represents a single // shape table index, so the fontset_table stores the shape table index, // and the shape_table_ must be consulted to obtain the actual unichar_id/ // font combinations that the shape represents. int ClassAndConfigIDToFontOrShapeID(int class_id, int int_result_config) const; // Converts a shape_table_ index to a classifier class_id index (not a // unichar-id!). Uses a search, so not fast. int ShapeIDToClassID(int shape_id) const; UNICHAR_ID *BaselineClassifier( TBLOB *Blob, const GenericVector& int_features, const INT_FX_RESULT_STRUCT& fx_info, ADAPT_TEMPLATES Templates, ADAPT_RESULTS *Results); int CharNormClassifier(TBLOB *blob, const TrainingSample& sample, ADAPT_RESULTS *adapt_results); // As CharNormClassifier, but operates on a TrainingSample and outputs to // a GenericVector of ShapeRating without conversion to classes. int CharNormTrainingSample(bool pruner_only, int keep_this, const TrainingSample& sample, GenericVector* results); UNICHAR_ID *GetAmbiguities(TBLOB *Blob, CLASS_ID CorrectClass); void DoAdaptiveMatch(TBLOB *Blob, ADAPT_RESULTS *Results); void AdaptToChar(TBLOB* Blob, CLASS_ID ClassId, int FontinfoId, FLOAT32 Threshold, ADAPT_TEMPLATES adaptive_templates); void DisplayAdaptedChar(TBLOB* blob, INT_CLASS_STRUCT* int_class); bool AdaptableWord(WERD_RES* word); void EndAdaptiveClassifier(); void SettupPass1(); void SettupPass2(); void AdaptiveClassifier(TBLOB *Blob, BLOB_CHOICE_LIST *Choices); void ClassifyAsNoise(ADAPT_RESULTS *Results); void ResetAdaptiveClassifierInternal(); void SwitchAdaptiveClassifier(); void StartBackupAdaptiveClassifier(); int GetCharNormFeature(const INT_FX_RESULT_STRUCT& fx_info, INT_TEMPLATES templates, uinT8* pruner_norm_array, uinT8* char_norm_array); // Computes the char_norm_array for the unicharset and, if not NULL, the // pruner_array as appropriate according to the existence of the shape_table. // The norm_feature is deleted as it is almost certainly no longer needed. void ComputeCharNormArrays(FEATURE_STRUCT* norm_feature, INT_TEMPLATES_STRUCT* templates, uinT8* char_norm_array, uinT8* pruner_array); bool TempConfigReliable(CLASS_ID class_id, const TEMP_CONFIG &config); void UpdateAmbigsGroup(CLASS_ID class_id, TBLOB *Blob); bool AdaptiveClassifierIsFull() const { return NumAdaptationsFailed > 0; } bool AdaptiveClassifierIsEmpty() const { return AdaptedTemplates->NumPermClasses == 0; } bool LooksLikeGarbage(TBLOB *blob); void RefreshDebugWindow(ScrollView **win, const char *msg, int y_offset, const TBOX &wbox); // intfx.cpp // Computes the DENORMS for bl(baseline) and cn(character) normalization // during feature extraction. The input denorm describes the current state // of the blob, which is usually a baseline-normalized word. // The Transforms setup are as follows: // Baseline Normalized (bl) Output: // We center the grapheme by aligning the x-coordinate of its centroid with // x=128 and leaving the already-baseline-normalized y as-is. // // Character Normalized (cn) Output: // We align the grapheme's centroid at the origin and scale it // asymmetrically in x and y so that the 2nd moments are a standard value // (51.2) ie the result is vaguely square. // If classify_nonlinear_norm is true: // A non-linear normalization is setup that attempts to evenly distribute // edges across x and y. // // Some of the fields of fx_info are also setup: // Length: Total length of outline. // Rx: Rounded y second moment. (Reversed by convention.) // Ry: rounded x second moment. // Xmean: Rounded x center of mass of the blob. // Ymean: Rounded y center of mass of the blob. static void SetupBLCNDenorms(const TBLOB& blob, bool nonlinear_norm, DENORM* bl_denorm, DENORM* cn_denorm, INT_FX_RESULT_STRUCT* fx_info); // Extracts sets of 3-D features of length kStandardFeatureLength (=12.8), as // (x,y) position and angle as measured counterclockwise from the vector // <-1, 0>, from blob using two normalizations defined by bl_denorm and // cn_denorm. See SetpuBLCNDenorms for definitions. // If outline_cn_counts is not NULL, on return it contains the cumulative // number of cn features generated for each outline in the blob (in order). // Thus after the first outline, there were (*outline_cn_counts)[0] features, // after the second outline, there were (*outline_cn_counts)[1] features etc. static void ExtractFeatures(const TBLOB& blob, bool nonlinear_norm, GenericVector* bl_features, GenericVector* cn_features, INT_FX_RESULT_STRUCT* results, GenericVector* outline_cn_counts); /* float2int.cpp ************************************************************/ void ClearCharNormArray(uinT8* char_norm_array); void ComputeIntCharNormArray(const FEATURE_STRUCT& norm_feature, uinT8* char_norm_array); void ComputeIntFeatures(FEATURE_SET Features, INT_FEATURE_ARRAY IntFeatures); /* intproto.cpp *************************************************************/ INT_TEMPLATES ReadIntTemplates(FILE *File); void WriteIntTemplates(FILE *File, INT_TEMPLATES Templates, const UNICHARSET& target_unicharset); CLASS_ID GetClassToDebug(const char *Prompt, bool* adaptive_on, bool* pretrained_on, int* shape_id); void ShowMatchDisplay(); /* font detection ***********************************************************/ UnicityTable& get_fontinfo_table() { return fontinfo_table_; } const UnicityTable& get_fontinfo_table() const { return fontinfo_table_; } UnicityTable& get_fontset_table() { return fontset_table_; } /* mfoutline.cpp ***********************************************************/ void NormalizeOutlines(LIST Outlines, FLOAT32 *XScale, FLOAT32 *YScale); /* outfeat.cpp ***********************************************************/ FEATURE_SET ExtractOutlineFeatures(TBLOB *Blob); /* picofeat.cpp ***********************************************************/ FEATURE_SET ExtractPicoFeatures(TBLOB *Blob); FEATURE_SET ExtractIntCNFeatures(const TBLOB& blob, const INT_FX_RESULT_STRUCT& fx_info); FEATURE_SET ExtractIntGeoFeatures(const TBLOB& blob, const INT_FX_RESULT_STRUCT& fx_info); /* blobclass.cpp ***********************************************************/ // Extracts features from the given blob and saves them in the tr_file_data_ // member variable. // fontname: Name of font that this blob was printed in. // cn_denorm: Character normalization transformation to apply to the blob. // fx_info: Character normalization parameters computed with cn_denorm. // blob_text: Ground truth text for the blob. void LearnBlob(const STRING& fontname, TBLOB* Blob, const DENORM& cn_denorm, const INT_FX_RESULT_STRUCT& fx_info, const char* blob_text); // Writes stored training data to a .tr file based on the given filename. // Returns false on error. bool WriteTRFile(const STRING& filename); // Member variables. // Parameters. // Set during training (in lang.config) to indicate whether the divisible // blobs chopper should be used (true for latin script.) BOOL_VAR_H(allow_blob_division, true, "Use divisible blobs chopping"); // Set during training (in lang.config) to indicate whether the divisible // blobs chopper should be used in preference to chopping. Set to true for // southern Indic scripts. BOOL_VAR_H(prioritize_division, FALSE, "Prioritize blob division over chopping"); INT_VAR_H(tessedit_single_match, FALSE, "Top choice only from CP"); BOOL_VAR_H(classify_enable_learning, true, "Enable adaptive classifier"); INT_VAR_H(classify_debug_level, 0, "Classify debug level"); /* mfoutline.cpp ***********************************************************/ /* control knobs used to control normalization of outlines */ INT_VAR_H(classify_norm_method, character, "Normalization Method ..."); double_VAR_H(classify_char_norm_range, 0.2, "Character Normalization Range ..."); double_VAR_H(classify_min_norm_scale_x, 0.0, "Min char x-norm scale ..."); double_VAR_H(classify_max_norm_scale_x, 0.325, "Max char x-norm scale ..."); double_VAR_H(classify_min_norm_scale_y, 0.0, "Min char y-norm scale ..."); double_VAR_H(classify_max_norm_scale_y, 0.325, "Max char y-norm scale ..."); double_VAR_H(classify_max_rating_ratio, 1.5, "Veto ratio between classifier ratings"); double_VAR_H(classify_max_certainty_margin, 5.5, "Veto difference between classifier certainties"); /* adaptmatch.cpp ***********************************************************/ BOOL_VAR_H(tess_cn_matching, 0, "Character Normalized Matching"); BOOL_VAR_H(tess_bn_matching, 0, "Baseline Normalized Matching"); BOOL_VAR_H(classify_enable_adaptive_matcher, 1, "Enable adaptive classifier"); BOOL_VAR_H(classify_use_pre_adapted_templates, 0, "Use pre-adapted classifier templates"); BOOL_VAR_H(classify_save_adapted_templates, 0, "Save adapted templates to a file"); BOOL_VAR_H(classify_enable_adaptive_debugger, 0, "Enable match debugger"); BOOL_VAR_H(classify_nonlinear_norm, 0, "Non-linear stroke-density normalization"); INT_VAR_H(matcher_debug_level, 0, "Matcher Debug Level"); INT_VAR_H(matcher_debug_flags, 0, "Matcher Debug Flags"); INT_VAR_H(classify_learning_debug_level, 0, "Learning Debug Level: "); double_VAR_H(matcher_good_threshold, 0.125, "Good Match (0-1)"); double_VAR_H(matcher_reliable_adaptive_result, 0.0, "Great Match (0-1)"); double_VAR_H(matcher_perfect_threshold, 0.02, "Perfect Match (0-1)"); double_VAR_H(matcher_bad_match_pad, 0.15, "Bad Match Pad (0-1)"); double_VAR_H(matcher_rating_margin, 0.1, "New template margin (0-1)"); double_VAR_H(matcher_avg_noise_size, 12.0, "Avg. noise blob length: "); INT_VAR_H(matcher_permanent_classes_min, 1, "Min # of permanent classes"); INT_VAR_H(matcher_min_examples_for_prototyping, 3, "Reliable Config Threshold"); INT_VAR_H(matcher_sufficient_examples_for_prototyping, 5, "Enable adaption even if the ambiguities have not been seen"); double_VAR_H(matcher_clustering_max_angle_delta, 0.015, "Maximum angle delta for prototype clustering"); double_VAR_H(classify_misfit_junk_penalty, 0.0, "Penalty to apply when a non-alnum is vertically out of " "its expected textline position"); double_VAR_H(rating_scale, 1.5, "Rating scaling factor"); double_VAR_H(certainty_scale, 20.0, "Certainty scaling factor"); double_VAR_H(tessedit_class_miss_scale, 0.00390625, "Scale factor for features not used"); double_VAR_H(classify_adapted_pruning_factor, 2.5, "Prune poor adapted results this much worse than best result"); double_VAR_H(classify_adapted_pruning_threshold, -1.0, "Threshold at which classify_adapted_pruning_factor starts"); INT_VAR_H(classify_adapt_proto_threshold, 230, "Threshold for good protos during adaptive 0-255"); INT_VAR_H(classify_adapt_feature_threshold, 230, "Threshold for good features during adaptive 0-255"); BOOL_VAR_H(disable_character_fragments, TRUE, "Do not include character fragments in the" " results of the classifier"); double_VAR_H(classify_character_fragments_garbage_certainty_threshold, -3.0, "Exclude fragments that do not match any whole character" " with at least this certainty"); BOOL_VAR_H(classify_debug_character_fragments, FALSE, "Bring up graphical debugging windows for fragments training"); BOOL_VAR_H(matcher_debug_separate_windows, FALSE, "Use two different windows for debugging the matching: " "One for the protos and one for the features."); STRING_VAR_H(classify_learn_debug_str, "", "Class str to debug learning"); /* intmatcher.cpp **********************************************************/ INT_VAR_H(classify_class_pruner_threshold, 229, "Class Pruner Threshold 0-255"); INT_VAR_H(classify_class_pruner_multiplier, 15, "Class Pruner Multiplier 0-255: "); INT_VAR_H(classify_cp_cutoff_strength, 7, "Class Pruner CutoffStrength: "); INT_VAR_H(classify_integer_matcher_multiplier, 10, "Integer Matcher Multiplier 0-255: "); // Use class variables to hold onto built-in templates and adapted templates. INT_TEMPLATES PreTrainedTemplates; ADAPT_TEMPLATES AdaptedTemplates; // The backup adapted templates are created from the previous page (only) // so they are always ready and reasonably well trained if the primary // adapted templates become full. ADAPT_TEMPLATES BackupAdaptedTemplates; // Create dummy proto and config masks for use with the built-in templates. BIT_VECTOR AllProtosOn; BIT_VECTOR AllConfigsOn; BIT_VECTOR AllConfigsOff; BIT_VECTOR TempProtoMask; bool EnableLearning; /* normmatch.cpp */ NORM_PROTOS *NormProtos; /* font detection ***********************************************************/ UnicityTable fontinfo_table_; // Without shape training, each class_id, config pair represents a single // unichar id/font combination, so each fontset_table_ entry holds font ids // for each config in the class. // With shape training, each class_id, config pair represents a single // shape_table_ index, so the fontset_table_ stores the shape_table_ index, // and the shape_table_ must be consulted to obtain the actual unichar_id/ // font combinations that the shape represents. UnicityTable fontset_table_; INT_VAR_H(il1_adaption_test, 0, "Don't adapt to i/I at beginning of word"); BOOL_VAR_H(classify_bln_numeric_mode, 0, "Assume the input is numbers [0-9]."); double_VAR_H(speckle_large_max_size, 0.30, "Max large speckle size"); double_VAR_H(speckle_rating_penalty, 10.0, "Penalty to add to worst rating for noise"); protected: IntegerMatcher im_; FEATURE_DEFS_STRUCT feature_defs_; // If a shape_table_ is present, it is used to remap classifier output in // ExpandShapesAndApplyCorrections. font_ids referenced by configs actually // mean an index to the shape_table_ and the choices returned are *all* the // shape_table_ entries at that index. ShapeTable* shape_table_; private: Dict dict_; // The currently active static classifier. ShapeClassifier* static_classifier_; /* variables used to hold performance statistics */ int NumAdaptationsFailed; // Training data gathered here for all the images in a document. STRING tr_file_data_; // Expected number of features in the class pruner, used to penalize // unknowns that have too few features (like a c being classified as e) so // it doesn't recognize everything as '@' or '#'. // CharNormCutoffs is for the static classifier (with no shapetable). // BaselineCutoffs gets a copy of CharNormCutoffs as an estimate of the real // value in the adaptive classifier. Both are indexed by unichar_id. // shapetable_cutoffs_ provides a similar value for each shape in the // shape_table_ uinT16* CharNormCutoffs; uinT16* BaselineCutoffs; GenericVector shapetable_cutoffs_; ScrollView* learn_debug_win_; ScrollView* learn_fragmented_word_debug_win_; ScrollView* learn_fragments_debug_win_; }; } // namespace tesseract #endif // TESSERACT_CLASSIFY_CLASSIFY_H__ tesseract-3.04.01/classify/cluster.cpp000066400000000000000000003052661266071204500176750ustar00rootroot00000000000000/****************************************************************************** ** Filename: cluster.c ** Purpose: Routines for clustering points in N-D space ** Author: Dan Johnson ** History: 5/29/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 "const.h" #include "cluster.h" #include "emalloc.h" #include "genericheap.h" #include "helpers.h" #include "kdpair.h" #include "matrix.h" #include "tprintf.h" #include "danerror.h" #include "freelist.h" #include #define HOTELLING 1 // If true use Hotelling's test to decide where to split. #define FTABLE_X 10 // Size of FTable. #define FTABLE_Y 100 // Size of FTable. // Table of values approximating the cumulative F-distribution for a confidence of 1%. const double FTable[FTABLE_Y][FTABLE_X] = { {4052.19, 4999.52, 5403.34, 5624.62, 5763.65, 5858.97, 5928.33, 5981.10, 6022.50, 6055.85,}, {98.502, 99.000, 99.166, 99.249, 99.300, 99.333, 99.356, 99.374, 99.388, 99.399,}, {34.116, 30.816, 29.457, 28.710, 28.237, 27.911, 27.672, 27.489, 27.345, 27.229,}, {21.198, 18.000, 16.694, 15.977, 15.522, 15.207, 14.976, 14.799, 14.659, 14.546,}, {16.258, 13.274, 12.060, 11.392, 10.967, 10.672, 10.456, 10.289, 10.158, 10.051,}, {13.745, 10.925, 9.780, 9.148, 8.746, 8.466, 8.260, 8.102, 7.976, 7.874,}, {12.246, 9.547, 8.451, 7.847, 7.460, 7.191, 6.993, 6.840, 6.719, 6.620,}, {11.259, 8.649, 7.591, 7.006, 6.632, 6.371, 6.178, 6.029, 5.911, 5.814,}, {10.561, 8.022, 6.992, 6.422, 6.057, 5.802, 5.613, 5.467, 5.351, 5.257,}, {10.044, 7.559, 6.552, 5.994, 5.636, 5.386, 5.200, 5.057, 4.942, 4.849,}, { 9.646, 7.206, 6.217, 5.668, 5.316, 5.069, 4.886, 4.744, 4.632, 4.539,}, { 9.330, 6.927, 5.953, 5.412, 5.064, 4.821, 4.640, 4.499, 4.388, 4.296,}, { 9.074, 6.701, 5.739, 5.205, 4.862, 4.620, 4.441, 4.302, 4.191, 4.100,}, { 8.862, 6.515, 5.564, 5.035, 4.695, 4.456, 4.278, 4.140, 4.030, 3.939,}, { 8.683, 6.359, 5.417, 4.893, 4.556, 4.318, 4.142, 4.004, 3.895, 3.805,}, { 8.531, 6.226, 5.292, 4.773, 4.437, 4.202, 4.026, 3.890, 3.780, 3.691,}, { 8.400, 6.112, 5.185, 4.669, 4.336, 4.102, 3.927, 3.791, 3.682, 3.593,}, { 8.285, 6.013, 5.092, 4.579, 4.248, 4.015, 3.841, 3.705, 3.597, 3.508,}, { 8.185, 5.926, 5.010, 4.500, 4.171, 3.939, 3.765, 3.631, 3.523, 3.434,}, { 8.096, 5.849, 4.938, 4.431, 4.103, 3.871, 3.699, 3.564, 3.457, 3.368,}, { 8.017, 5.780, 4.874, 4.369, 4.042, 3.812, 3.640, 3.506, 3.398, 3.310,}, { 7.945, 5.719, 4.817, 4.313, 3.988, 3.758, 3.587, 3.453, 3.346, 3.258,}, { 7.881, 5.664, 4.765, 4.264, 3.939, 3.710, 3.539, 3.406, 3.299, 3.211,}, { 7.823, 5.614, 4.718, 4.218, 3.895, 3.667, 3.496, 3.363, 3.256, 3.168,}, { 7.770, 5.568, 4.675, 4.177, 3.855, 3.627, 3.457, 3.324, 3.217, 3.129,}, { 7.721, 5.526, 4.637, 4.140, 3.818, 3.591, 3.421, 3.288, 3.182, 3.094,}, { 7.677, 5.488, 4.601, 4.106, 3.785, 3.558, 3.388, 3.256, 3.149, 3.062,}, { 7.636, 5.453, 4.568, 4.074, 3.754, 3.528, 3.358, 3.226, 3.120, 3.032,}, { 7.598, 5.420, 4.538, 4.045, 3.725, 3.499, 3.330, 3.198, 3.092, 3.005,}, { 7.562, 5.390, 4.510, 4.018, 3.699, 3.473, 3.305, 3.173, 3.067, 2.979,}, { 7.530, 5.362, 4.484, 3.993, 3.675, 3.449, 3.281, 3.149, 3.043, 2.955,}, { 7.499, 5.336, 4.459, 3.969, 3.652, 3.427, 3.258, 3.127, 3.021, 2.934,}, { 7.471, 5.312, 4.437, 3.948, 3.630, 3.406, 3.238, 3.106, 3.000, 2.913,}, { 7.444, 5.289, 4.416, 3.927, 3.611, 3.386, 3.218, 3.087, 2.981, 2.894,}, { 7.419, 5.268, 4.396, 3.908, 3.592, 3.368, 3.200, 3.069, 2.963, 2.876,}, { 7.396, 5.248, 4.377, 3.890, 3.574, 3.351, 3.183, 3.052, 2.946, 2.859,}, { 7.373, 5.229, 4.360, 3.873, 3.558, 3.334, 3.167, 3.036, 2.930, 2.843,}, { 7.353, 5.211, 4.343, 3.858, 3.542, 3.319, 3.152, 3.021, 2.915, 2.828,}, { 7.333, 5.194, 4.327, 3.843, 3.528, 3.305, 3.137, 3.006, 2.901, 2.814,}, { 7.314, 5.179, 4.313, 3.828, 3.514, 3.291, 3.124, 2.993, 2.888, 2.801,}, { 7.296, 5.163, 4.299, 3.815, 3.501, 3.278, 3.111, 2.980, 2.875, 2.788,}, { 7.280, 5.149, 4.285, 3.802, 3.488, 3.266, 3.099, 2.968, 2.863, 2.776,}, { 7.264, 5.136, 4.273, 3.790, 3.476, 3.254, 3.087, 2.957, 2.851, 2.764,}, { 7.248, 5.123, 4.261, 3.778, 3.465, 3.243, 3.076, 2.946, 2.840, 2.754,}, { 7.234, 5.110, 4.249, 3.767, 3.454, 3.232, 3.066, 2.935, 2.830, 2.743,}, { 7.220, 5.099, 4.238, 3.757, 3.444, 3.222, 3.056, 2.925, 2.820, 2.733,}, { 7.207, 5.087, 4.228, 3.747, 3.434, 3.213, 3.046, 2.916, 2.811, 2.724,}, { 7.194, 5.077, 4.218, 3.737, 3.425, 3.204, 3.037, 2.907, 2.802, 2.715,}, { 7.182, 5.066, 4.208, 3.728, 3.416, 3.195, 3.028, 2.898, 2.793, 2.706,}, { 7.171, 5.057, 4.199, 3.720, 3.408, 3.186, 3.020, 2.890, 2.785, 2.698,}, { 7.159, 5.047, 4.191, 3.711, 3.400, 3.178, 3.012, 2.882, 2.777, 2.690,}, { 7.149, 5.038, 4.182, 3.703, 3.392, 3.171, 3.005, 2.874, 2.769, 2.683,}, { 7.139, 5.030, 4.174, 3.695, 3.384, 3.163, 2.997, 2.867, 2.762, 2.675,}, { 7.129, 5.021, 4.167, 3.688, 3.377, 3.156, 2.990, 2.860, 2.755, 2.668,}, { 7.119, 5.013, 4.159, 3.681, 3.370, 3.149, 2.983, 2.853, 2.748, 2.662,}, { 7.110, 5.006, 4.152, 3.674, 3.363, 3.143, 2.977, 2.847, 2.742, 2.655,}, { 7.102, 4.998, 4.145, 3.667, 3.357, 3.136, 2.971, 2.841, 2.736, 2.649,}, { 7.093, 4.991, 4.138, 3.661, 3.351, 3.130, 2.965, 2.835, 2.730, 2.643,}, { 7.085, 4.984, 4.132, 3.655, 3.345, 3.124, 2.959, 2.829, 2.724, 2.637,}, { 7.077, 4.977, 4.126, 3.649, 3.339, 3.119, 2.953, 2.823, 2.718, 2.632,}, { 7.070, 4.971, 4.120, 3.643, 3.333, 3.113, 2.948, 2.818, 2.713, 2.626,}, { 7.062, 4.965, 4.114, 3.638, 3.328, 3.108, 2.942, 2.813, 2.708, 2.621,}, { 7.055, 4.959, 4.109, 3.632, 3.323, 3.103, 2.937, 2.808, 2.703, 2.616,}, { 7.048, 4.953, 4.103, 3.627, 3.318, 3.098, 2.932, 2.803, 2.698, 2.611,}, { 7.042, 4.947, 4.098, 3.622, 3.313, 3.093, 2.928, 2.798, 2.693, 2.607,}, { 7.035, 4.942, 4.093, 3.618, 3.308, 3.088, 2.923, 2.793, 2.689, 2.602,}, { 7.029, 4.937, 4.088, 3.613, 3.304, 3.084, 2.919, 2.789, 2.684, 2.598,}, { 7.023, 4.932, 4.083, 3.608, 3.299, 3.080, 2.914, 2.785, 2.680, 2.593,}, { 7.017, 4.927, 4.079, 3.604, 3.295, 3.075, 2.910, 2.781, 2.676, 2.589,}, { 7.011, 4.922, 4.074, 3.600, 3.291, 3.071, 2.906, 2.777, 2.672, 2.585,}, { 7.006, 4.917, 4.070, 3.596, 3.287, 3.067, 2.902, 2.773, 2.668, 2.581,}, { 7.001, 4.913, 4.066, 3.591, 3.283, 3.063, 2.898, 2.769, 2.664, 2.578,}, { 6.995, 4.908, 4.062, 3.588, 3.279, 3.060, 2.895, 2.765, 2.660, 2.574,}, { 6.990, 4.904, 4.058, 3.584, 3.275, 3.056, 2.891, 2.762, 2.657, 2.570,}, { 6.985, 4.900, 4.054, 3.580, 3.272, 3.052, 2.887, 2.758, 2.653, 2.567,}, { 6.981, 4.896, 4.050, 3.577, 3.268, 3.049, 2.884, 2.755, 2.650, 2.563,}, { 6.976, 4.892, 4.047, 3.573, 3.265, 3.046, 2.881, 2.751, 2.647, 2.560,}, { 6.971, 4.888, 4.043, 3.570, 3.261, 3.042, 2.877, 2.748, 2.644, 2.557,}, { 6.967, 4.884, 4.040, 3.566, 3.258, 3.039, 2.874, 2.745, 2.640, 2.554,}, { 6.963, 4.881, 4.036, 3.563, 3.255, 3.036, 2.871, 2.742, 2.637, 2.551,}, { 6.958, 4.877, 4.033, 3.560, 3.252, 3.033, 2.868, 2.739, 2.634, 2.548,}, { 6.954, 4.874, 4.030, 3.557, 3.249, 3.030, 2.865, 2.736, 2.632, 2.545,}, { 6.950, 4.870, 4.027, 3.554, 3.246, 3.027, 2.863, 2.733, 2.629, 2.542,}, { 6.947, 4.867, 4.024, 3.551, 3.243, 3.025, 2.860, 2.731, 2.626, 2.539,}, { 6.943, 4.864, 4.021, 3.548, 3.240, 3.022, 2.857, 2.728, 2.623, 2.537,}, { 6.939, 4.861, 4.018, 3.545, 3.238, 3.019, 2.854, 2.725, 2.621, 2.534,}, { 6.935, 4.858, 4.015, 3.543, 3.235, 3.017, 2.852, 2.723, 2.618, 2.532,}, { 6.932, 4.855, 4.012, 3.540, 3.233, 3.014, 2.849, 2.720, 2.616, 2.529,}, { 6.928, 4.852, 4.010, 3.538, 3.230, 3.012, 2.847, 2.718, 2.613, 2.527,}, { 6.925, 4.849, 4.007, 3.535, 3.228, 3.009, 2.845, 2.715, 2.611, 2.524,}, { 6.922, 4.846, 4.004, 3.533, 3.225, 3.007, 2.842, 2.713, 2.609, 2.522,}, { 6.919, 4.844, 4.002, 3.530, 3.223, 3.004, 2.840, 2.711, 2.606, 2.520,}, { 6.915, 4.841, 3.999, 3.528, 3.221, 3.002, 2.838, 2.709, 2.604, 2.518,}, { 6.912, 4.838, 3.997, 3.525, 3.218, 3.000, 2.835, 2.706, 2.602, 2.515,}, { 6.909, 4.836, 3.995, 3.523, 3.216, 2.998, 2.833, 2.704, 2.600, 2.513,}, { 6.906, 4.833, 3.992, 3.521, 3.214, 2.996, 2.831, 2.702, 2.598, 2.511,}, { 6.904, 4.831, 3.990, 3.519, 3.212, 2.994, 2.829, 2.700, 2.596, 2.509,}, { 6.901, 4.829, 3.988, 3.517, 3.210, 2.992, 2.827, 2.698, 2.594, 2.507,}, { 6.898, 4.826, 3.986, 3.515, 3.208, 2.990, 2.825, 2.696, 2.592, 2.505,}, { 6.895, 4.824, 3.984, 3.513, 3.206, 2.988, 2.823, 2.694, 2.590, 2.503} }; /** define the variance which will be used as a minimum variance for any dimension of any feature. Since most features are calculated from numbers with a precision no better than 1 in 128, the variance should never be less than the square of this number for parameters whose range is 1. */ #define MINVARIANCE 0.0004 /** define the absolute minimum number of samples which must be present in order to accurately test hypotheses about underlying probability distributions. Define separately the minimum samples that are needed before a statistical analysis is attempted; this number should be equal to MINSAMPLES but can be set to a lower number for early testing when very few samples are available. */ #define MINSAMPLESPERBUCKET 5 #define MINSAMPLES (MINBUCKETS * MINSAMPLESPERBUCKET) #define MINSAMPLESNEEDED 1 /** define the size of the table which maps normalized samples to histogram buckets. Also define the number of standard deviations in a normal distribution which are considered to be significant. The mapping table will be defined in such a way that it covers the specified number of standard deviations on either side of the mean. BUCKETTABLESIZE should always be even. */ #define BUCKETTABLESIZE 1024 #define NORMALEXTENT 3.0 struct TEMPCLUSTER { CLUSTER *Cluster; CLUSTER *Neighbor; }; typedef tesseract::KDPairInc ClusterPair; typedef tesseract::GenericHeap ClusterHeap; struct STATISTICS { FLOAT32 AvgVariance; FLOAT32 *CoVariance; FLOAT32 *Min; // largest negative distance from the mean FLOAT32 *Max; // largest positive distance from the mean }; struct BUCKETS { DISTRIBUTION Distribution; // distribution being tested for uinT32 SampleCount; // # of samples in histogram FLOAT64 Confidence; // confidence level of test FLOAT64 ChiSquared; // test threshold uinT16 NumberOfBuckets; // number of cells in histogram uinT16 Bucket[BUCKETTABLESIZE];// mapping to histogram buckets uinT32 *Count; // frequency of occurrence histogram FLOAT32 *ExpectedCount; // expected histogram }; struct CHISTRUCT{ uinT16 DegreesOfFreedom; FLOAT64 Alpha; FLOAT64 ChiSquared; }; // For use with KDWalk / MakePotentialClusters struct ClusteringContext { ClusterHeap *heap; // heap used to hold temp clusters, "best" on top TEMPCLUSTER *candidates; // array of potential clusters KDTREE *tree; // kd-tree to be searched for neighbors inT32 next; // next candidate to be used }; typedef FLOAT64 (*DENSITYFUNC) (inT32); typedef FLOAT64 (*SOLVEFUNC) (CHISTRUCT *, double); #define Odd(N) ((N)%2) #define Mirror(N,R) ((R) - (N) - 1) #define Abs(N) ( ( (N) < 0 ) ? ( -(N) ) : (N) ) //--------------Global Data Definitions and Declarations---------------------- /** the following variables describe a discrete normal distribution which is used by NormalDensity() and NormalBucket(). The constant NORMALEXTENT determines how many standard deviations of the distribution are mapped onto the fixed discrete range of x. x=0 is mapped to -NORMALEXTENT standard deviations and x=BUCKETTABLESIZE is mapped to +NORMALEXTENT standard deviations. */ #define SqrtOf2Pi 2.506628275 static const FLOAT64 kNormalStdDev = BUCKETTABLESIZE / (2.0 * NORMALEXTENT); static const FLOAT64 kNormalVariance = (BUCKETTABLESIZE * BUCKETTABLESIZE) / (4.0 * NORMALEXTENT * NORMALEXTENT); static const FLOAT64 kNormalMagnitude = (2.0 * NORMALEXTENT) / (SqrtOf2Pi * BUCKETTABLESIZE); static const FLOAT64 kNormalMean = BUCKETTABLESIZE / 2; /** define lookup tables used to compute the number of histogram buckets that should be used for a given number of samples. */ #define LOOKUPTABLESIZE 8 #define MAXDEGREESOFFREEDOM MAXBUCKETS static const uinT32 kCountTable[LOOKUPTABLESIZE] = { MINSAMPLES, 200, 400, 600, 800, 1000, 1500, 2000 }; // number of samples static const uinT16 kBucketsTable[LOOKUPTABLESIZE] = { MINBUCKETS, 16, 20, 24, 27, 30, 35, MAXBUCKETS }; // number of buckets /*------------------------------------------------------------------------- Private Function Prototypes --------------------------------------------------------------------------*/ void CreateClusterTree(CLUSTERER *Clusterer); void MakePotentialClusters(ClusteringContext *context, CLUSTER *Cluster, inT32 Level); CLUSTER *FindNearestNeighbor(KDTREE *Tree, CLUSTER *Cluster, FLOAT32 *Distance); CLUSTER *MakeNewCluster(CLUSTERER *Clusterer, TEMPCLUSTER *TempCluster); inT32 MergeClusters (inT16 N, register PARAM_DESC ParamDesc[], register inT32 n1, register inT32 n2, register FLOAT32 m[], register FLOAT32 m1[], register FLOAT32 m2[]); void ComputePrototypes(CLUSTERER *Clusterer, CLUSTERCONFIG *Config); PROTOTYPE *MakePrototype(CLUSTERER *Clusterer, CLUSTERCONFIG *Config, CLUSTER *Cluster); PROTOTYPE *MakeDegenerateProto(uinT16 N, CLUSTER *Cluster, STATISTICS *Statistics, PROTOSTYLE Style, inT32 MinSamples); PROTOTYPE *TestEllipticalProto(CLUSTERER *Clusterer, CLUSTERCONFIG *Config, CLUSTER *Cluster, STATISTICS *Statistics); PROTOTYPE *MakeSphericalProto(CLUSTERER *Clusterer, CLUSTER *Cluster, STATISTICS *Statistics, BUCKETS *Buckets); PROTOTYPE *MakeEllipticalProto(CLUSTERER *Clusterer, CLUSTER *Cluster, STATISTICS *Statistics, BUCKETS *Buckets); PROTOTYPE *MakeMixedProto(CLUSTERER *Clusterer, CLUSTER *Cluster, STATISTICS *Statistics, BUCKETS *NormalBuckets, FLOAT64 Confidence); void MakeDimRandom(uinT16 i, PROTOTYPE *Proto, PARAM_DESC *ParamDesc); void MakeDimUniform(uinT16 i, PROTOTYPE *Proto, STATISTICS *Statistics); STATISTICS *ComputeStatistics (inT16 N, PARAM_DESC ParamDesc[], CLUSTER * Cluster); PROTOTYPE *NewSphericalProto(uinT16 N, CLUSTER *Cluster, STATISTICS *Statistics); PROTOTYPE *NewEllipticalProto(inT16 N, CLUSTER *Cluster, STATISTICS *Statistics); PROTOTYPE *NewMixedProto(inT16 N, CLUSTER *Cluster, STATISTICS *Statistics); PROTOTYPE *NewSimpleProto(inT16 N, CLUSTER *Cluster); BOOL8 Independent (PARAM_DESC ParamDesc[], inT16 N, FLOAT32 * CoVariance, FLOAT32 Independence); BUCKETS *GetBuckets(CLUSTERER* clusterer, DISTRIBUTION Distribution, uinT32 SampleCount, FLOAT64 Confidence); BUCKETS *MakeBuckets(DISTRIBUTION Distribution, uinT32 SampleCount, FLOAT64 Confidence); uinT16 OptimumNumberOfBuckets(uinT32 SampleCount); FLOAT64 ComputeChiSquared(uinT16 DegreesOfFreedom, FLOAT64 Alpha); FLOAT64 NormalDensity(inT32 x); FLOAT64 UniformDensity(inT32 x); FLOAT64 Integral(FLOAT64 f1, FLOAT64 f2, FLOAT64 Dx); void FillBuckets(BUCKETS *Buckets, CLUSTER *Cluster, uinT16 Dim, PARAM_DESC *ParamDesc, FLOAT32 Mean, FLOAT32 StdDev); uinT16 NormalBucket(PARAM_DESC *ParamDesc, FLOAT32 x, FLOAT32 Mean, FLOAT32 StdDev); uinT16 UniformBucket(PARAM_DESC *ParamDesc, FLOAT32 x, FLOAT32 Mean, FLOAT32 StdDev); BOOL8 DistributionOK(BUCKETS *Buckets); void FreeStatistics(STATISTICS *Statistics); void FreeBuckets(BUCKETS *Buckets); void FreeCluster(CLUSTER *Cluster); uinT16 DegreesOfFreedom(DISTRIBUTION Distribution, uinT16 HistogramBuckets); int NumBucketsMatch(void *arg1, // BUCKETS *Histogram, void *arg2); // uinT16 *DesiredNumberOfBuckets); int ListEntryMatch(void *arg1, void *arg2); void AdjustBuckets(BUCKETS *Buckets, uinT32 NewSampleCount); void InitBuckets(BUCKETS *Buckets); int AlphaMatch(void *arg1, // CHISTRUCT *ChiStruct, void *arg2); // CHISTRUCT *SearchKey); CHISTRUCT *NewChiStruct(uinT16 DegreesOfFreedom, FLOAT64 Alpha); FLOAT64 Solve(SOLVEFUNC Function, void *FunctionParams, FLOAT64 InitialGuess, FLOAT64 Accuracy); FLOAT64 ChiArea(CHISTRUCT *ChiParams, FLOAT64 x); BOOL8 MultipleCharSamples(CLUSTERER *Clusterer, CLUSTER *Cluster, FLOAT32 MaxIllegal); double InvertMatrix(const float* input, int size, float* inv); //--------------------------Public Code-------------------------------------- /** * This routine creates a new clusterer data structure, * initializes it, and returns a pointer to it. * * @param SampleSize number of dimensions in feature space * @param ParamDesc description of each dimension * @return pointer to the new clusterer data structure * @note Exceptions: None * @note History: 5/29/89, DSJ, Created. */ CLUSTERER * MakeClusterer (inT16 SampleSize, const PARAM_DESC ParamDesc[]) { CLUSTERER *Clusterer; int i; // allocate main clusterer data structure and init simple fields Clusterer = (CLUSTERER *) Emalloc (sizeof (CLUSTERER)); Clusterer->SampleSize = SampleSize; Clusterer->NumberOfSamples = 0; Clusterer->NumChar = 0; // init fields which will not be used initially Clusterer->Root = NULL; Clusterer->ProtoList = NIL_LIST; // maintain a copy of param descriptors in the clusterer data structure Clusterer->ParamDesc = (PARAM_DESC *) Emalloc (SampleSize * sizeof (PARAM_DESC)); for (i = 0; i < SampleSize; i++) { Clusterer->ParamDesc[i].Circular = ParamDesc[i].Circular; Clusterer->ParamDesc[i].NonEssential = ParamDesc[i].NonEssential; Clusterer->ParamDesc[i].Min = ParamDesc[i].Min; Clusterer->ParamDesc[i].Max = ParamDesc[i].Max; Clusterer->ParamDesc[i].Range = ParamDesc[i].Max - ParamDesc[i].Min; Clusterer->ParamDesc[i].HalfRange = Clusterer->ParamDesc[i].Range / 2; Clusterer->ParamDesc[i].MidRange = (ParamDesc[i].Max + ParamDesc[i].Min) / 2; } // allocate a kd tree to hold the samples Clusterer->KDTree = MakeKDTree (SampleSize, ParamDesc); // Initialize cache of histogram buckets to minimize recomputing them. for (int d = 0; d < DISTRIBUTION_COUNT; ++d) { for (int c = 0; c < MAXBUCKETS + 1 - MINBUCKETS; ++c) Clusterer->bucket_cache[d][c] = NULL; } return Clusterer; } // MakeClusterer /** * This routine creates a new sample data structure to hold * the specified feature. This sample is added to the clusterer * data structure (so that it knows which samples are to be * clustered later), and a pointer to the sample is returned to * the caller. * * @param Clusterer clusterer data structure to add sample to * @param Feature feature to be added to clusterer * @param CharID unique ident. of char that sample came from * * @return Pointer to the new sample data structure * @note Exceptions: ALREADYCLUSTERED MakeSample can't be called after * ClusterSamples has been called * @note History: 5/29/89, DSJ, Created. */ SAMPLE* MakeSample(CLUSTERER * Clusterer, const FLOAT32* Feature, inT32 CharID) { SAMPLE *Sample; int i; // see if the samples have already been clustered - if so trap an error if (Clusterer->Root != NULL) DoError (ALREADYCLUSTERED, "Can't add samples after they have been clustered"); // allocate the new sample and initialize it Sample = (SAMPLE *) Emalloc (sizeof (SAMPLE) + (Clusterer->SampleSize - 1) * sizeof (FLOAT32)); Sample->Clustered = FALSE; Sample->Prototype = FALSE; Sample->SampleCount = 1; Sample->Left = NULL; Sample->Right = NULL; Sample->CharID = CharID; for (i = 0; i < Clusterer->SampleSize; i++) Sample->Mean[i] = Feature[i]; // add the sample to the KD tree - keep track of the total # of samples Clusterer->NumberOfSamples++; KDStore (Clusterer->KDTree, Sample->Mean, (char *) Sample); if (CharID >= Clusterer->NumChar) Clusterer->NumChar = CharID + 1; // execute hook for monitoring clustering operation // (*SampleCreationHook)( Sample ); return (Sample); } // MakeSample /** * This routine first checks to see if the samples in this * clusterer have already been clustered before; if so, it does * not bother to recreate the cluster tree. It simply recomputes * the prototypes based on the new Config info. * * If the samples have not been clustered before, the * samples in the KD tree are formed into a cluster tree and then * the prototypes are computed from the cluster tree. * * In either case this routine returns a pointer to a * list of prototypes that best represent the samples given * the constraints specified in Config. * * @param Clusterer data struct containing samples to be clustered * @param Config parameters which control clustering process * * @return Pointer to a list of prototypes * @note Exceptions: None * @note History: 5/29/89, DSJ, Created. */ LIST ClusterSamples(CLUSTERER *Clusterer, CLUSTERCONFIG *Config) { //only create cluster tree if samples have never been clustered before if (Clusterer->Root == NULL) CreateClusterTree(Clusterer); //deallocate the old prototype list if one exists FreeProtoList (&Clusterer->ProtoList); Clusterer->ProtoList = NIL_LIST; //compute prototypes starting at the root node in the tree ComputePrototypes(Clusterer, Config); return (Clusterer->ProtoList); } // ClusterSamples /** * This routine frees all of the memory allocated to the * specified data structure. It will not, however, free * the memory used by the prototype list. The pointers to * the clusters for each prototype in the list will be set * to NULL to indicate that the cluster data structures no * longer exist. Any sample lists that have been obtained * via calls to GetSamples are no longer valid. * @param Clusterer pointer to data structure to be freed * @return None * @note Exceptions: None * @note History: 6/6/89, DSJ, Created. */ void FreeClusterer(CLUSTERER *Clusterer) { if (Clusterer != NULL) { memfree (Clusterer->ParamDesc); if (Clusterer->KDTree != NULL) FreeKDTree (Clusterer->KDTree); if (Clusterer->Root != NULL) FreeCluster (Clusterer->Root); // Free up all used buckets structures. for (int d = 0; d < DISTRIBUTION_COUNT; ++d) { for (int c = 0; c < MAXBUCKETS + 1 - MINBUCKETS; ++c) if (Clusterer->bucket_cache[d][c] != NULL) FreeBuckets(Clusterer->bucket_cache[d][c]); } memfree(Clusterer); } } // FreeClusterer /** * This routine frees all of the memory allocated to the * specified list of prototypes. The clusters which are * pointed to by the prototypes are not freed. * @param ProtoList pointer to list of prototypes to be freed * @return None * @note Exceptions: None * @note History: 6/6/89, DSJ, Created. */ void FreeProtoList(LIST *ProtoList) { destroy_nodes(*ProtoList, FreePrototype); } // FreeProtoList /** * This routine deallocates the memory consumed by the specified * prototype and modifies the corresponding cluster so that it * is no longer marked as a prototype. The cluster is NOT * deallocated by this routine. * @param arg prototype data structure to be deallocated * @return None * @note Exceptions: None * @note History: 5/30/89, DSJ, Created. */ void FreePrototype(void *arg) { //PROTOTYPE *Prototype) PROTOTYPE *Prototype = (PROTOTYPE *) arg; // unmark the corresponding cluster (if there is one if (Prototype->Cluster != NULL) Prototype->Cluster->Prototype = FALSE; // deallocate the prototype statistics and then the prototype itself if (Prototype->Distrib != NULL) memfree (Prototype->Distrib); if (Prototype->Mean != NULL) memfree (Prototype->Mean); if (Prototype->Style != spherical) { if (Prototype->Variance.Elliptical != NULL) memfree (Prototype->Variance.Elliptical); if (Prototype->Magnitude.Elliptical != NULL) memfree (Prototype->Magnitude.Elliptical); if (Prototype->Weight.Elliptical != NULL) memfree (Prototype->Weight.Elliptical); } memfree(Prototype); } // FreePrototype /** * This routine is used to find all of the samples which * belong to a cluster. It starts by removing the top * cluster on the cluster list (SearchState). If this cluster is * a leaf it is returned. Otherwise, the right subcluster * is pushed on the list and we continue the search in the * left subcluster. This continues until a leaf is found. * If all samples have been found, NULL is returned. * InitSampleSearch() must be called * before NextSample() to initialize the search. * @param SearchState ptr to list containing clusters to be searched * @return Pointer to the next leaf cluster (sample) or NULL. * @note Exceptions: None * @note History: 6/16/89, DSJ, Created. */ CLUSTER *NextSample(LIST *SearchState) { CLUSTER *Cluster; if (*SearchState == NIL_LIST) return (NULL); Cluster = (CLUSTER *) first_node (*SearchState); *SearchState = pop (*SearchState); while (TRUE) { if (Cluster->Left == NULL) return (Cluster); *SearchState = push (*SearchState, Cluster->Right); Cluster = Cluster->Left; } } // NextSample /** * This routine returns the mean of the specified * prototype in the indicated dimension. * @param Proto prototype to return mean of * @param Dimension dimension whose mean is to be returned * @return Mean of Prototype in Dimension * @note Exceptions: none * @note History: 7/6/89, DSJ, Created. */ FLOAT32 Mean(PROTOTYPE *Proto, uinT16 Dimension) { return (Proto->Mean[Dimension]); } // Mean /** * This routine returns the standard deviation of the * prototype in the indicated dimension. * @param Proto prototype to return standard deviation of * @param Dimension dimension whose stddev is to be returned * @return Standard deviation of Prototype in Dimension * @note Exceptions: none * @note History: 7/6/89, DSJ, Created. */ FLOAT32 StandardDeviation(PROTOTYPE *Proto, uinT16 Dimension) { switch (Proto->Style) { case spherical: return ((FLOAT32) sqrt ((double) Proto->Variance.Spherical)); case elliptical: return ((FLOAT32) sqrt ((double) Proto->Variance.Elliptical[Dimension])); case mixed: switch (Proto->Distrib[Dimension]) { case normal: return ((FLOAT32) sqrt ((double) Proto->Variance.Elliptical[Dimension])); case uniform: case D_random: return (Proto->Variance.Elliptical[Dimension]); case DISTRIBUTION_COUNT: ASSERT_HOST(!"Distribution count not allowed!"); } } return 0.0f; } // StandardDeviation /*--------------------------------------------------------------------------- Private Code ----------------------------------------------------------------------------*/ /** * This routine performs a bottoms-up clustering on the samples * held in the kd-tree of the Clusterer data structure. The * result is a cluster tree. Each node in the tree represents * a cluster which conceptually contains a subset of the samples. * More precisely, the cluster contains all of the samples which * are contained in its two sub-clusters. The leaves of the * tree are the individual samples themselves; they have no * sub-clusters. The root node of the tree conceptually contains * all of the samples. * @param Clusterer data structure holdings samples to be clustered * @return None (the Clusterer data structure is changed) * @note Exceptions: None * @note History: 5/29/89, DSJ, Created. */ void CreateClusterTree(CLUSTERER *Clusterer) { ClusteringContext context; ClusterPair HeapEntry; TEMPCLUSTER *PotentialCluster; // each sample and its nearest neighbor form a "potential" cluster // save these in a heap with the "best" potential clusters on top context.tree = Clusterer->KDTree; context.candidates = (TEMPCLUSTER *) Emalloc(Clusterer->NumberOfSamples * sizeof(TEMPCLUSTER)); context.next = 0; context.heap = new ClusterHeap(Clusterer->NumberOfSamples); KDWalk(context.tree, (void_proc)MakePotentialClusters, &context); // form potential clusters into actual clusters - always do "best" first while (context.heap->Pop(&HeapEntry)) { PotentialCluster = HeapEntry.data; // if main cluster of potential cluster is already in another cluster // then we don't need to worry about it if (PotentialCluster->Cluster->Clustered) { continue; } // if main cluster is not yet clustered, but its nearest neighbor is // then we must find a new nearest neighbor else if (PotentialCluster->Neighbor->Clustered) { PotentialCluster->Neighbor = FindNearestNeighbor(context.tree, PotentialCluster->Cluster, &HeapEntry.key); if (PotentialCluster->Neighbor != NULL) { context.heap->Push(&HeapEntry); } } // if neither cluster is already clustered, form permanent cluster else { PotentialCluster->Cluster = MakeNewCluster(Clusterer, PotentialCluster); PotentialCluster->Neighbor = FindNearestNeighbor(context.tree, PotentialCluster->Cluster, &HeapEntry.key); if (PotentialCluster->Neighbor != NULL) { context.heap->Push(&HeapEntry); } } } // the root node in the cluster tree is now the only node in the kd-tree Clusterer->Root = (CLUSTER *) RootOf(Clusterer->KDTree); // free up the memory used by the K-D tree, heap, and temp clusters FreeKDTree(context.tree); Clusterer->KDTree = NULL; delete context.heap; memfree(context.candidates); } // CreateClusterTree /** * This routine is designed to be used in concert with the * KDWalk routine. It will create a potential cluster for * each sample in the kd-tree that is being walked. This * potential cluster will then be pushed on the heap. * @param context ClusteringContext (see definition above) * @param Cluster current cluster being visited in kd-tree walk * @param Level level of this cluster in the kd-tree */ void MakePotentialClusters(ClusteringContext *context, CLUSTER *Cluster, inT32 Level) { ClusterPair HeapEntry; int next = context->next; context->candidates[next].Cluster = Cluster; HeapEntry.data = &(context->candidates[next]); context->candidates[next].Neighbor = FindNearestNeighbor(context->tree, context->candidates[next].Cluster, &HeapEntry.key); if (context->candidates[next].Neighbor != NULL) { context->heap->Push(&HeapEntry); context->next++; } } // MakePotentialClusters /** * This routine searches the specified kd-tree for the nearest * neighbor of the specified cluster. It actually uses the * kd routines to find the 2 nearest neighbors since one of them * will be the original cluster. A pointer to the nearest * neighbor is returned, if it can be found, otherwise NULL is * returned. The distance between the 2 nodes is placed * in the specified variable. * @param Tree kd-tree to search in for nearest neighbor * @param Cluster cluster whose nearest neighbor is to be found * @param Distance ptr to variable to report distance found * @return Pointer to the nearest neighbor of Cluster, or NULL * @note Exceptions: none * @note History: 5/29/89, DSJ, Created. * 7/13/89, DSJ, Removed visibility of kd-tree node data struct */ CLUSTER * FindNearestNeighbor(KDTREE * Tree, CLUSTER * Cluster, FLOAT32 * Distance) #define MAXNEIGHBORS 2 #define MAXDISTANCE MAX_FLOAT32 { CLUSTER *Neighbor[MAXNEIGHBORS]; FLOAT32 Dist[MAXNEIGHBORS]; int NumberOfNeighbors; inT32 i; CLUSTER *BestNeighbor; // find the 2 nearest neighbors of the cluster KDNearestNeighborSearch(Tree, Cluster->Mean, MAXNEIGHBORS, MAXDISTANCE, &NumberOfNeighbors, (void **)Neighbor, Dist); // search for the nearest neighbor that is not the cluster itself *Distance = MAXDISTANCE; BestNeighbor = NULL; for (i = 0; i < NumberOfNeighbors; i++) { if ((Dist[i] < *Distance) && (Neighbor[i] != Cluster)) { *Distance = Dist[i]; BestNeighbor = Neighbor[i]; } } return BestNeighbor; } // FindNearestNeighbor /** * This routine creates a new permanent cluster from the * clusters specified in TempCluster. The 2 clusters in * TempCluster are marked as "clustered" and deleted from * the kd-tree. The new cluster is then added to the kd-tree. * @param Clusterer current clustering environment * @param TempCluster potential cluster to make permanent * @return Pointer to the new permanent cluster * @note Exceptions: none * @note History: 5/29/89, DSJ, Created. * 7/13/89, DSJ, Removed visibility of kd-tree node data struct */ CLUSTER *MakeNewCluster(CLUSTERER *Clusterer, TEMPCLUSTER *TempCluster) { CLUSTER *Cluster; // allocate the new cluster and initialize it Cluster = (CLUSTER *) Emalloc( sizeof(CLUSTER) + (Clusterer->SampleSize - 1) * sizeof(FLOAT32)); Cluster->Clustered = FALSE; Cluster->Prototype = FALSE; Cluster->Left = TempCluster->Cluster; Cluster->Right = TempCluster->Neighbor; Cluster->CharID = -1; // mark the old clusters as "clustered" and delete them from the kd-tree Cluster->Left->Clustered = TRUE; Cluster->Right->Clustered = TRUE; KDDelete(Clusterer->KDTree, Cluster->Left->Mean, Cluster->Left); KDDelete(Clusterer->KDTree, Cluster->Right->Mean, Cluster->Right); // compute the mean and sample count for the new cluster Cluster->SampleCount = MergeClusters(Clusterer->SampleSize, Clusterer->ParamDesc, Cluster->Left->SampleCount, Cluster->Right->SampleCount, Cluster->Mean, Cluster->Left->Mean, Cluster->Right->Mean); // add the new cluster to the KD tree KDStore(Clusterer->KDTree, Cluster->Mean, Cluster); return Cluster; } // MakeNewCluster /** * This routine merges two clusters into one larger cluster. * To do this it computes the number of samples in the new * cluster and the mean of the new cluster. The ParamDesc * information is used to ensure that circular dimensions * are handled correctly. * @param N # of dimensions (size of arrays) * @param ParamDesc array of dimension descriptions * @param n1, n2 number of samples in each old cluster * @param m array to hold mean of new cluster * @param m1, m2 arrays containing means of old clusters * @return The number of samples in the new cluster. * @note Exceptions: None * @note History: 5/31/89, DSJ, Created. */ inT32 MergeClusters(inT16 N, PARAM_DESC ParamDesc[], inT32 n1, inT32 n2, FLOAT32 m[], FLOAT32 m1[], FLOAT32 m2[]) { inT32 i, n; n = n1 + n2; for (i = N; i > 0; i--, ParamDesc++, m++, m1++, m2++) { if (ParamDesc->Circular) { // if distance between means is greater than allowed // reduce upper point by one "rotation" to compute mean // then normalize the mean back into the accepted range if ((*m2 - *m1) > ParamDesc->HalfRange) { *m = (n1 * *m1 + n2 * (*m2 - ParamDesc->Range)) / n; if (*m < ParamDesc->Min) *m += ParamDesc->Range; } else if ((*m1 - *m2) > ParamDesc->HalfRange) { *m = (n1 * (*m1 - ParamDesc->Range) + n2 * *m2) / n; if (*m < ParamDesc->Min) *m += ParamDesc->Range; } else *m = (n1 * *m1 + n2 * *m2) / n; } else *m = (n1 * *m1 + n2 * *m2) / n; } return n; } // MergeClusters /** * This routine decides which clusters in the cluster tree * should be represented by prototypes, forms a list of these * prototypes, and places the list in the Clusterer data * structure. * @param Clusterer data structure holding cluster tree * @param Config parameters used to control prototype generation * @return None * @note Exceptions: None * @note History: 5/30/89, DSJ, Created. */ void ComputePrototypes(CLUSTERER *Clusterer, CLUSTERCONFIG *Config) { LIST ClusterStack = NIL_LIST; CLUSTER *Cluster; PROTOTYPE *Prototype; // use a stack to keep track of clusters waiting to be processed // initially the only cluster on the stack is the root cluster if (Clusterer->Root != NULL) ClusterStack = push (NIL_LIST, Clusterer->Root); // loop until we have analyzed all clusters which are potential prototypes while (ClusterStack != NIL_LIST) { // remove the next cluster to be analyzed from the stack // try to make a prototype from the cluster // if successful, put it on the proto list, else split the cluster Cluster = (CLUSTER *) first_node (ClusterStack); ClusterStack = pop (ClusterStack); Prototype = MakePrototype(Clusterer, Config, Cluster); if (Prototype != NULL) { Clusterer->ProtoList = push (Clusterer->ProtoList, Prototype); } else { ClusterStack = push (ClusterStack, Cluster->Right); ClusterStack = push (ClusterStack, Cluster->Left); } } } // ComputePrototypes /** * This routine attempts to create a prototype from the * specified cluster that conforms to the distribution * specified in Config. If there are too few samples in the * cluster to perform a statistical analysis, then a prototype * is generated but labelled as insignificant. If the * dimensions of the cluster are not independent, no prototype * is generated and NULL is returned. If a prototype can be * found that matches the desired distribution then a pointer * to it is returned, otherwise NULL is returned. * @param Clusterer data structure holding cluster tree * @param Config parameters used to control prototype generation * @param Cluster cluster to be made into a prototype * @return Pointer to new prototype or NULL * @note Exceptions: None * @note History: 6/19/89, DSJ, Created. */ PROTOTYPE *MakePrototype(CLUSTERER *Clusterer, CLUSTERCONFIG *Config, CLUSTER *Cluster) { STATISTICS *Statistics; PROTOTYPE *Proto; BUCKETS *Buckets; // filter out clusters which contain samples from the same character if (MultipleCharSamples (Clusterer, Cluster, Config->MaxIllegal)) return NULL; // compute the covariance matrix and ranges for the cluster Statistics = ComputeStatistics(Clusterer->SampleSize, Clusterer->ParamDesc, Cluster); // check for degenerate clusters which need not be analyzed further // note that the MinSamples test assumes that all clusters with multiple // character samples have been removed (as above) Proto = MakeDegenerateProto( Clusterer->SampleSize, Cluster, Statistics, Config->ProtoStyle, (inT32) (Config->MinSamples * Clusterer->NumChar)); if (Proto != NULL) { FreeStatistics(Statistics); return Proto; } // check to ensure that all dimensions are independent if (!Independent(Clusterer->ParamDesc, Clusterer->SampleSize, Statistics->CoVariance, Config->Independence)) { FreeStatistics(Statistics); return NULL; } if (HOTELLING && Config->ProtoStyle == elliptical) { Proto = TestEllipticalProto(Clusterer, Config, Cluster, Statistics); if (Proto != NULL) { FreeStatistics(Statistics); return Proto; } } // create a histogram data structure used to evaluate distributions Buckets = GetBuckets(Clusterer, normal, Cluster->SampleCount, Config->Confidence); // create a prototype based on the statistics and test it switch (Config->ProtoStyle) { case spherical: Proto = MakeSphericalProto(Clusterer, Cluster, Statistics, Buckets); break; case elliptical: Proto = MakeEllipticalProto(Clusterer, Cluster, Statistics, Buckets); break; case mixed: Proto = MakeMixedProto(Clusterer, Cluster, Statistics, Buckets, Config->Confidence); break; case automatic: Proto = MakeSphericalProto(Clusterer, Cluster, Statistics, Buckets); if (Proto != NULL) break; Proto = MakeEllipticalProto(Clusterer, Cluster, Statistics, Buckets); if (Proto != NULL) break; Proto = MakeMixedProto(Clusterer, Cluster, Statistics, Buckets, Config->Confidence); break; } FreeStatistics(Statistics); return Proto; } // MakePrototype /** * This routine checks for clusters which are degenerate and * therefore cannot be analyzed in a statistically valid way. * A cluster is defined as degenerate if it does not have at * least MINSAMPLESNEEDED samples in it. If the cluster is * found to be degenerate, a prototype of the specified style * is generated and marked as insignificant. A cluster is * also degenerate if it does not have at least MinSamples * samples in it. * * If the cluster is not degenerate, NULL is returned. * * @param N number of dimensions * @param Cluster cluster being analyzed * @param Statistics statistical info about cluster * @param Style type of prototype to be generated * @param MinSamples minimum number of samples in a cluster * @return Pointer to degenerate prototype or NULL. * @note Exceptions: None * @note History: 6/20/89, DSJ, Created. * 7/12/89, DSJ, Changed name and added check for 0 stddev. * 8/8/89, DSJ, Removed check for 0 stddev (handled elsewhere). */ PROTOTYPE *MakeDegenerateProto( //this was MinSample uinT16 N, CLUSTER *Cluster, STATISTICS *Statistics, PROTOSTYLE Style, inT32 MinSamples) { PROTOTYPE *Proto = NULL; if (MinSamples < MINSAMPLESNEEDED) MinSamples = MINSAMPLESNEEDED; if (Cluster->SampleCount < MinSamples) { switch (Style) { case spherical: Proto = NewSphericalProto (N, Cluster, Statistics); break; case elliptical: case automatic: Proto = NewEllipticalProto (N, Cluster, Statistics); break; case mixed: Proto = NewMixedProto (N, Cluster, Statistics); break; } Proto->Significant = FALSE; } return (Proto); } // MakeDegenerateProto /** * This routine tests the specified cluster to see if ** * there is a statistically significant difference between * the sub-clusters that would be made if the cluster were to * be split. If not, then a new prototype is formed and * returned to the caller. If there is, then NULL is returned * to the caller. * @param Clusterer data struct containing samples being clustered * @param Config provides the magic number of samples that make a good cluster * @param Cluster cluster to be made into an elliptical prototype * @param Statistics statistical info about cluster * @return Pointer to new elliptical prototype or NULL. */ PROTOTYPE *TestEllipticalProto(CLUSTERER *Clusterer, CLUSTERCONFIG *Config, CLUSTER *Cluster, STATISTICS *Statistics) { // Fraction of the number of samples used as a range around 1 within // which a cluster has the magic size that allows a boost to the // FTable by kFTableBoostMargin, thus allowing clusters near the // magic size (equal to the number of sample characters) to be more // likely to stay together. const double kMagicSampleMargin = 0.0625; const double kFTableBoostMargin = 2.0; int N = Clusterer->SampleSize; CLUSTER* Left = Cluster->Left; CLUSTER* Right = Cluster->Right; if (Left == NULL || Right == NULL) return NULL; int TotalDims = Left->SampleCount + Right->SampleCount; if (TotalDims < N + 1 || TotalDims < 2) return NULL; const int kMatrixSize = N * N * sizeof(FLOAT32); FLOAT32* Covariance = reinterpret_cast(Emalloc(kMatrixSize)); FLOAT32* Inverse = reinterpret_cast(Emalloc(kMatrixSize)); FLOAT32* Delta = reinterpret_cast(Emalloc(N * sizeof(FLOAT32))); // Compute a new covariance matrix that only uses essential features. for (int i = 0; i < N; ++i) { int row_offset = i * N; if (!Clusterer->ParamDesc[i].NonEssential) { for (int j = 0; j < N; ++j) { if (!Clusterer->ParamDesc[j].NonEssential) Covariance[j + row_offset] = Statistics->CoVariance[j + row_offset]; else Covariance[j + row_offset] = 0.0f; } } else { for (int j = 0; j < N; ++j) { if (i == j) Covariance[j + row_offset] = 1.0f; else Covariance[j + row_offset] = 0.0f; } } } double err = InvertMatrix(Covariance, N, Inverse); if (err > 1) { tprintf("Clustering error: Matrix inverse failed with error %g\n", err); } int EssentialN = 0; for (int dim = 0; dim < N; ++dim) { if (!Clusterer->ParamDesc[dim].NonEssential) { Delta[dim] = Left->Mean[dim] - Right->Mean[dim]; ++EssentialN; } else { Delta[dim] = 0.0f; } } // Compute Hotelling's T-squared. double Tsq = 0.0; for (int x = 0; x < N; ++x) { double temp = 0.0; for (int y = 0; y < N; ++y) { temp += Inverse[y + N*x] * Delta[y]; } Tsq += Delta[x] * temp; } memfree(Covariance); memfree(Inverse); memfree(Delta); // Changed this function to match the formula in // Statistical Methods in Medical Research p 473 // By Peter Armitage, Geoffrey Berry, J. N. S. Matthews. // Tsq *= Left->SampleCount * Right->SampleCount / TotalDims; double F = Tsq * (TotalDims - EssentialN - 1) / ((TotalDims - 2)*EssentialN); int Fx = EssentialN; if (Fx > FTABLE_X) Fx = FTABLE_X; --Fx; int Fy = TotalDims - EssentialN - 1; if (Fy > FTABLE_Y) Fy = FTABLE_Y; --Fy; double FTarget = FTable[Fy][Fx]; if (Config->MagicSamples > 0 && TotalDims >= Config->MagicSamples * (1.0 - kMagicSampleMargin) && TotalDims <= Config->MagicSamples * (1.0 + kMagicSampleMargin)) { // Give magic-sized clusters a magic FTable boost. FTarget += kFTableBoostMargin; } if (F < FTarget) { return NewEllipticalProto (Clusterer->SampleSize, Cluster, Statistics); } return NULL; } /** * This routine tests the specified cluster to see if it can * be approximated by a spherical normal distribution. If it * can be, then a new prototype is formed and returned to the * caller. If it can't be, then NULL is returned to the caller. * @param Clusterer data struct containing samples being clustered * @param Cluster cluster to be made into a spherical prototype * @param Statistics statistical info about cluster * @param Buckets histogram struct used to analyze distribution * @return Pointer to new spherical prototype or NULL. * @note Exceptions: None * @note History: 6/1/89, DSJ, Created. */ PROTOTYPE *MakeSphericalProto(CLUSTERER *Clusterer, CLUSTER *Cluster, STATISTICS *Statistics, BUCKETS *Buckets) { PROTOTYPE *Proto = NULL; int i; // check that each dimension is a normal distribution for (i = 0; i < Clusterer->SampleSize; i++) { if (Clusterer->ParamDesc[i].NonEssential) continue; FillBuckets (Buckets, Cluster, i, &(Clusterer->ParamDesc[i]), Cluster->Mean[i], sqrt ((FLOAT64) (Statistics->AvgVariance))); if (!DistributionOK (Buckets)) break; } // if all dimensions matched a normal distribution, make a proto if (i >= Clusterer->SampleSize) Proto = NewSphericalProto (Clusterer->SampleSize, Cluster, Statistics); return (Proto); } // MakeSphericalProto /** * This routine tests the specified cluster to see if it can * be approximated by an elliptical normal distribution. If it * can be, then a new prototype is formed and returned to the * caller. If it can't be, then NULL is returned to the caller. * @param Clusterer data struct containing samples being clustered * @param Cluster cluster to be made into an elliptical prototype * @param Statistics statistical info about cluster * @param Buckets histogram struct used to analyze distribution * @return Pointer to new elliptical prototype or NULL. * @note Exceptions: None * @note History: 6/12/89, DSJ, Created. */ PROTOTYPE *MakeEllipticalProto(CLUSTERER *Clusterer, CLUSTER *Cluster, STATISTICS *Statistics, BUCKETS *Buckets) { PROTOTYPE *Proto = NULL; int i; // check that each dimension is a normal distribution for (i = 0; i < Clusterer->SampleSize; i++) { if (Clusterer->ParamDesc[i].NonEssential) continue; FillBuckets (Buckets, Cluster, i, &(Clusterer->ParamDesc[i]), Cluster->Mean[i], sqrt ((FLOAT64) Statistics-> CoVariance[i * (Clusterer->SampleSize + 1)])); if (!DistributionOK (Buckets)) break; } // if all dimensions matched a normal distribution, make a proto if (i >= Clusterer->SampleSize) Proto = NewEllipticalProto (Clusterer->SampleSize, Cluster, Statistics); return (Proto); } // MakeEllipticalProto /** * This routine tests each dimension of the specified cluster to * see what distribution would best approximate that dimension. * Each dimension is compared to the following distributions * in order: normal, random, uniform. If each dimension can * be represented by one of these distributions, * then a new prototype is formed and returned to the * caller. If it can't be, then NULL is returned to the caller. * @param Clusterer data struct containing samples being clustered * @param Cluster cluster to be made into a prototype * @param Statistics statistical info about cluster * @param NormalBuckets histogram struct used to analyze distribution * @param Confidence confidence level for alternate distributions * @return Pointer to new mixed prototype or NULL. * @note Exceptions: None * @note History: 6/12/89, DSJ, Created. */ PROTOTYPE *MakeMixedProto(CLUSTERER *Clusterer, CLUSTER *Cluster, STATISTICS *Statistics, BUCKETS *NormalBuckets, FLOAT64 Confidence) { PROTOTYPE *Proto; int i; BUCKETS *UniformBuckets = NULL; BUCKETS *RandomBuckets = NULL; // create a mixed proto to work on - initially assume all dimensions normal*/ Proto = NewMixedProto (Clusterer->SampleSize, Cluster, Statistics); // find the proper distribution for each dimension for (i = 0; i < Clusterer->SampleSize; i++) { if (Clusterer->ParamDesc[i].NonEssential) continue; FillBuckets (NormalBuckets, Cluster, i, &(Clusterer->ParamDesc[i]), Proto->Mean[i], sqrt ((FLOAT64) Proto->Variance.Elliptical[i])); if (DistributionOK (NormalBuckets)) continue; if (RandomBuckets == NULL) RandomBuckets = GetBuckets(Clusterer, D_random, Cluster->SampleCount, Confidence); MakeDimRandom (i, Proto, &(Clusterer->ParamDesc[i])); FillBuckets (RandomBuckets, Cluster, i, &(Clusterer->ParamDesc[i]), Proto->Mean[i], Proto->Variance.Elliptical[i]); if (DistributionOK (RandomBuckets)) continue; if (UniformBuckets == NULL) UniformBuckets = GetBuckets(Clusterer, uniform, Cluster->SampleCount, Confidence); MakeDimUniform(i, Proto, Statistics); FillBuckets (UniformBuckets, Cluster, i, &(Clusterer->ParamDesc[i]), Proto->Mean[i], Proto->Variance.Elliptical[i]); if (DistributionOK (UniformBuckets)) continue; break; } // if any dimension failed to match a distribution, discard the proto if (i < Clusterer->SampleSize) { FreePrototype(Proto); Proto = NULL; } return (Proto); } // MakeMixedProto /** * This routine alters the ith dimension of the specified * mixed prototype to be D_random. * @param i index of dimension to be changed * @param Proto prototype whose dimension is to be altered * @param ParamDesc description of specified dimension * @return None * @note Exceptions: None * @note History: 6/20/89, DSJ, Created. */ void MakeDimRandom(uinT16 i, PROTOTYPE *Proto, PARAM_DESC *ParamDesc) { Proto->Distrib[i] = D_random; Proto->Mean[i] = ParamDesc->MidRange; Proto->Variance.Elliptical[i] = ParamDesc->HalfRange; // subtract out the previous magnitude of this dimension from the total Proto->TotalMagnitude /= Proto->Magnitude.Elliptical[i]; Proto->Magnitude.Elliptical[i] = 1.0 / ParamDesc->Range; Proto->TotalMagnitude *= Proto->Magnitude.Elliptical[i]; Proto->LogMagnitude = log ((double) Proto->TotalMagnitude); // note that the proto Weight is irrelevant for D_random protos } // MakeDimRandom /** * This routine alters the ith dimension of the specified * mixed prototype to be uniform. * @param i index of dimension to be changed * @param Proto prototype whose dimension is to be altered * @param Statistics statistical info about prototype * @return None * @note Exceptions: None * @note History: 6/20/89, DSJ, Created. */ void MakeDimUniform(uinT16 i, PROTOTYPE *Proto, STATISTICS *Statistics) { Proto->Distrib[i] = uniform; Proto->Mean[i] = Proto->Cluster->Mean[i] + (Statistics->Min[i] + Statistics->Max[i]) / 2; Proto->Variance.Elliptical[i] = (Statistics->Max[i] - Statistics->Min[i]) / 2; if (Proto->Variance.Elliptical[i] < MINVARIANCE) Proto->Variance.Elliptical[i] = MINVARIANCE; // subtract out the previous magnitude of this dimension from the total Proto->TotalMagnitude /= Proto->Magnitude.Elliptical[i]; Proto->Magnitude.Elliptical[i] = 1.0 / (2.0 * Proto->Variance.Elliptical[i]); Proto->TotalMagnitude *= Proto->Magnitude.Elliptical[i]; Proto->LogMagnitude = log ((double) Proto->TotalMagnitude); // note that the proto Weight is irrelevant for uniform protos } // MakeDimUniform /** * This routine searches the cluster tree for all leaf nodes * which are samples in the specified cluster. It computes * a full covariance matrix for these samples as well as * keeping track of the ranges (min and max) for each * dimension. A special data structure is allocated to * return this information to the caller. An incremental * algorithm for computing statistics is not used because * it will not work with circular dimensions. * @param N number of dimensions * @param ParamDesc array of dimension descriptions * @param Cluster cluster whose stats are to be computed * @return Pointer to new data structure containing statistics * @note Exceptions: None * @note History: 6/2/89, DSJ, Created. */ STATISTICS * ComputeStatistics (inT16 N, PARAM_DESC ParamDesc[], CLUSTER * Cluster) { STATISTICS *Statistics; int i, j; FLOAT32 *CoVariance; FLOAT32 *Distance; LIST SearchState; SAMPLE *Sample; uinT32 SampleCountAdjustedForBias; // allocate memory to hold the statistics results Statistics = (STATISTICS *) Emalloc (sizeof (STATISTICS)); Statistics->CoVariance = (FLOAT32 *) Emalloc (N * N * sizeof (FLOAT32)); Statistics->Min = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); Statistics->Max = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); // allocate temporary memory to hold the sample to mean distances Distance = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); // initialize the statistics Statistics->AvgVariance = 1.0; CoVariance = Statistics->CoVariance; for (i = 0; i < N; i++) { Statistics->Min[i] = 0.0; Statistics->Max[i] = 0.0; for (j = 0; j < N; j++, CoVariance++) *CoVariance = 0; } // find each sample in the cluster and merge it into the statistics InitSampleSearch(SearchState, Cluster); while ((Sample = NextSample (&SearchState)) != NULL) { for (i = 0; i < N; i++) { Distance[i] = Sample->Mean[i] - Cluster->Mean[i]; if (ParamDesc[i].Circular) { if (Distance[i] > ParamDesc[i].HalfRange) Distance[i] -= ParamDesc[i].Range; if (Distance[i] < -ParamDesc[i].HalfRange) Distance[i] += ParamDesc[i].Range; } if (Distance[i] < Statistics->Min[i]) Statistics->Min[i] = Distance[i]; if (Distance[i] > Statistics->Max[i]) Statistics->Max[i] = Distance[i]; } CoVariance = Statistics->CoVariance; for (i = 0; i < N; i++) for (j = 0; j < N; j++, CoVariance++) *CoVariance += Distance[i] * Distance[j]; } // normalize the variances by the total number of samples // use SampleCount-1 instead of SampleCount to get an unbiased estimate // also compute the geometic mean of the diagonal variances // ensure that clusters with only 1 sample are handled correctly if (Cluster->SampleCount > 1) SampleCountAdjustedForBias = Cluster->SampleCount - 1; else SampleCountAdjustedForBias = 1; CoVariance = Statistics->CoVariance; for (i = 0; i < N; i++) for (j = 0; j < N; j++, CoVariance++) { *CoVariance /= SampleCountAdjustedForBias; if (j == i) { if (*CoVariance < MINVARIANCE) *CoVariance = MINVARIANCE; Statistics->AvgVariance *= *CoVariance; } } Statistics->AvgVariance = (float)pow((double)Statistics->AvgVariance, 1.0 / N); // release temporary memory and return memfree(Distance); return (Statistics); } // ComputeStatistics /** * This routine creates a spherical prototype data structure to * approximate the samples in the specified cluster. * Spherical prototypes have a single variance which is * common across all dimensions. All dimensions are normally * distributed and independent. * @param N number of dimensions * @param Cluster cluster to be made into a spherical prototype * @param Statistics statistical info about samples in cluster * @return Pointer to a new spherical prototype data structure * @note Exceptions: None * @note History: 6/19/89, DSJ, Created. */ PROTOTYPE *NewSphericalProto(uinT16 N, CLUSTER *Cluster, STATISTICS *Statistics) { PROTOTYPE *Proto; Proto = NewSimpleProto (N, Cluster); Proto->Variance.Spherical = Statistics->AvgVariance; if (Proto->Variance.Spherical < MINVARIANCE) Proto->Variance.Spherical = MINVARIANCE; Proto->Magnitude.Spherical = 1.0 / sqrt ((double) (2.0 * PI * Proto->Variance.Spherical)); Proto->TotalMagnitude = (float)pow((double)Proto->Magnitude.Spherical, (double) N); Proto->Weight.Spherical = 1.0 / Proto->Variance.Spherical; Proto->LogMagnitude = log ((double) Proto->TotalMagnitude); return (Proto); } // NewSphericalProto /** * This routine creates an elliptical prototype data structure to * approximate the samples in the specified cluster. * Elliptical prototypes have a variance for each dimension. * All dimensions are normally distributed and independent. * @param N number of dimensions * @param Cluster cluster to be made into an elliptical prototype * @param Statistics statistical info about samples in cluster * @return Pointer to a new elliptical prototype data structure * @note Exceptions: None * @note History: 6/19/89, DSJ, Created. */ PROTOTYPE *NewEllipticalProto(inT16 N, CLUSTER *Cluster, STATISTICS *Statistics) { PROTOTYPE *Proto; FLOAT32 *CoVariance; int i; Proto = NewSimpleProto (N, Cluster); Proto->Variance.Elliptical = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); Proto->Magnitude.Elliptical = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); Proto->Weight.Elliptical = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); CoVariance = Statistics->CoVariance; Proto->TotalMagnitude = 1.0; for (i = 0; i < N; i++, CoVariance += N + 1) { Proto->Variance.Elliptical[i] = *CoVariance; if (Proto->Variance.Elliptical[i] < MINVARIANCE) Proto->Variance.Elliptical[i] = MINVARIANCE; Proto->Magnitude.Elliptical[i] = 1.0 / sqrt ((double) (2.0 * PI * Proto->Variance.Elliptical[i])); Proto->Weight.Elliptical[i] = 1.0 / Proto->Variance.Elliptical[i]; Proto->TotalMagnitude *= Proto->Magnitude.Elliptical[i]; } Proto->LogMagnitude = log ((double) Proto->TotalMagnitude); Proto->Style = elliptical; return (Proto); } // NewEllipticalProto /** * This routine creates a mixed prototype data structure to * approximate the samples in the specified cluster. * Mixed prototypes can have different distributions for * each dimension. All dimensions are independent. The * structure is initially filled in as though it were an * elliptical prototype. The actual distributions of the * dimensions can be altered by other routines. * @param N number of dimensions * @param Cluster cluster to be made into a mixed prototype * @param Statistics statistical info about samples in cluster * @return Pointer to a new mixed prototype data structure * @note Exceptions: None * @note History: 6/19/89, DSJ, Created. */ PROTOTYPE *NewMixedProto(inT16 N, CLUSTER *Cluster, STATISTICS *Statistics) { PROTOTYPE *Proto; int i; Proto = NewEllipticalProto (N, Cluster, Statistics); Proto->Distrib = (DISTRIBUTION *) Emalloc (N * sizeof (DISTRIBUTION)); for (i = 0; i < N; i++) { Proto->Distrib[i] = normal; } Proto->Style = mixed; return (Proto); } // NewMixedProto /** * This routine allocates memory to hold a simple prototype * data structure, i.e. one without independent distributions * and variances for each dimension. * @param N number of dimensions * @param Cluster cluster to be made into a prototype * @return Pointer to new simple prototype * @note Exceptions: None * @note History: 6/19/89, DSJ, Created. */ PROTOTYPE *NewSimpleProto(inT16 N, CLUSTER *Cluster) { PROTOTYPE *Proto; int i; Proto = (PROTOTYPE *) Emalloc (sizeof (PROTOTYPE)); Proto->Mean = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); for (i = 0; i < N; i++) Proto->Mean[i] = Cluster->Mean[i]; Proto->Distrib = NULL; Proto->Significant = TRUE; Proto->Merged = FALSE; Proto->Style = spherical; Proto->NumSamples = Cluster->SampleCount; Proto->Cluster = Cluster; Proto->Cluster->Prototype = TRUE; return (Proto); } // NewSimpleProto /** * This routine returns TRUE if the specified covariance * matrix indicates that all N dimensions are independent of * one another. One dimension is judged to be independent of * another when the magnitude of the corresponding correlation * coefficient is * less than the specified Independence factor. The * correlation coefficient is calculated as: (see Duda and * Hart, pg. 247) * coeff[ij] = stddev[ij] / sqrt (stddev[ii] * stddev[jj]) * The covariance matrix is assumed to be symmetric (which * should always be true). * @param ParamDesc descriptions of each feature space dimension * @param N number of dimensions * @param CoVariance ptr to a covariance matrix * @param Independence max off-diagonal correlation coefficient * @return TRUE if dimensions are independent, FALSE otherwise * @note Exceptions: None * @note History: 6/4/89, DSJ, Created. */ BOOL8 Independent (PARAM_DESC ParamDesc[], inT16 N, FLOAT32 * CoVariance, FLOAT32 Independence) { int i, j; FLOAT32 *VARii; // points to ith on-diagonal element FLOAT32 *VARjj; // points to jth on-diagonal element FLOAT32 CorrelationCoeff; VARii = CoVariance; for (i = 0; i < N; i++, VARii += N + 1) { if (ParamDesc[i].NonEssential) continue; VARjj = VARii + N + 1; CoVariance = VARii + 1; for (j = i + 1; j < N; j++, CoVariance++, VARjj += N + 1) { if (ParamDesc[j].NonEssential) continue; if ((*VARii == 0.0) || (*VARjj == 0.0)) CorrelationCoeff = 0.0; else CorrelationCoeff = sqrt (sqrt (*CoVariance * *CoVariance / (*VARii * *VARjj))); if (CorrelationCoeff > Independence) return (FALSE); } } return (TRUE); } // Independent /** * This routine returns a histogram data structure which can * be used by other routines to place samples into histogram * buckets, and then apply a goodness of fit test to the * histogram data to determine if the samples belong to the * specified probability distribution. The routine keeps * a list of bucket data structures which have already been * created so that it minimizes the computation time needed * to create a new bucket. * @param clusterer which keeps a bucket_cache for us. * @param Distribution type of probability distribution to test for * @param SampleCount number of samples that are available * @param Confidence probability of a Type I error * @return Bucket data structure * @note Exceptions: none * @note History: Thu Aug 3 12:58:10 1989, DSJ, Created. */ BUCKETS *GetBuckets(CLUSTERER* clusterer, DISTRIBUTION Distribution, uinT32 SampleCount, FLOAT64 Confidence) { // Get an old bucket structure with the same number of buckets. uinT16 NumberOfBuckets = OptimumNumberOfBuckets(SampleCount); BUCKETS *Buckets = clusterer->bucket_cache[Distribution][NumberOfBuckets - MINBUCKETS]; // If a matching bucket structure is not found, make one and save it. if (Buckets == NULL) { Buckets = MakeBuckets(Distribution, SampleCount, Confidence); clusterer->bucket_cache[Distribution][NumberOfBuckets - MINBUCKETS] = Buckets; } else { // Just adjust the existing buckets. if (SampleCount != Buckets->SampleCount) AdjustBuckets(Buckets, SampleCount); if (Confidence != Buckets->Confidence) { Buckets->Confidence = Confidence; Buckets->ChiSquared = ComputeChiSquared( DegreesOfFreedom(Distribution, Buckets->NumberOfBuckets), Confidence); } InitBuckets(Buckets); } return Buckets; } // GetBuckets /** * This routine creates a histogram data structure which can * be used by other routines to place samples into histogram * buckets, and then apply a goodness of fit test to the * histogram data to determine if the samples belong to the * specified probability distribution. The buckets are * allocated in such a way that the expected frequency of * samples in each bucket is approximately the same. In * order to make this possible, a mapping table is * computed which maps "normalized" samples into the * appropriate bucket. * @param Distribution type of probability distribution to test for * @param SampleCount number of samples that are available * @param Confidence probability of a Type I error * @return Pointer to new histogram data structure * @note Exceptions: None * @note History: 6/4/89, DSJ, Created. */ BUCKETS *MakeBuckets(DISTRIBUTION Distribution, uinT32 SampleCount, FLOAT64 Confidence) { const DENSITYFUNC DensityFunction[] = { NormalDensity, UniformDensity, UniformDensity }; int i, j; BUCKETS *Buckets; FLOAT64 BucketProbability; FLOAT64 NextBucketBoundary; FLOAT64 Probability; FLOAT64 ProbabilityDelta; FLOAT64 LastProbDensity; FLOAT64 ProbDensity; uinT16 CurrentBucket; BOOL8 Symmetrical; // allocate memory needed for data structure Buckets = reinterpret_cast(Emalloc(sizeof(BUCKETS))); Buckets->NumberOfBuckets = OptimumNumberOfBuckets(SampleCount); Buckets->SampleCount = SampleCount; Buckets->Confidence = Confidence; Buckets->Count = reinterpret_cast( Emalloc(Buckets->NumberOfBuckets * sizeof(uinT32))); Buckets->ExpectedCount = reinterpret_cast( Emalloc(Buckets->NumberOfBuckets * sizeof(FLOAT32))); // initialize simple fields Buckets->Distribution = Distribution; for (i = 0; i < Buckets->NumberOfBuckets; i++) { Buckets->Count[i] = 0; Buckets->ExpectedCount[i] = 0.0; } // all currently defined distributions are symmetrical Symmetrical = TRUE; Buckets->ChiSquared = ComputeChiSquared( DegreesOfFreedom(Distribution, Buckets->NumberOfBuckets), Confidence); if (Symmetrical) { // allocate buckets so that all have approx. equal probability BucketProbability = 1.0 / (FLOAT64) (Buckets->NumberOfBuckets); // distribution is symmetric so fill in upper half then copy CurrentBucket = Buckets->NumberOfBuckets / 2; if (Odd (Buckets->NumberOfBuckets)) NextBucketBoundary = BucketProbability / 2; else NextBucketBoundary = BucketProbability; Probability = 0.0; LastProbDensity = (*DensityFunction[(int) Distribution]) (BUCKETTABLESIZE / 2); for (i = BUCKETTABLESIZE / 2; i < BUCKETTABLESIZE; i++) { ProbDensity = (*DensityFunction[(int) Distribution]) (i + 1); ProbabilityDelta = Integral (LastProbDensity, ProbDensity, 1.0); Probability += ProbabilityDelta; if (Probability > NextBucketBoundary) { if (CurrentBucket < Buckets->NumberOfBuckets - 1) CurrentBucket++; NextBucketBoundary += BucketProbability; } Buckets->Bucket[i] = CurrentBucket; Buckets->ExpectedCount[CurrentBucket] += (FLOAT32) (ProbabilityDelta * SampleCount); LastProbDensity = ProbDensity; } // place any leftover probability into the last bucket Buckets->ExpectedCount[CurrentBucket] += (FLOAT32) ((0.5 - Probability) * SampleCount); // copy upper half of distribution to lower half for (i = 0, j = BUCKETTABLESIZE - 1; i < j; i++, j--) Buckets->Bucket[i] = Mirror(Buckets->Bucket[j], Buckets->NumberOfBuckets); // copy upper half of expected counts to lower half for (i = 0, j = Buckets->NumberOfBuckets - 1; i <= j; i++, j--) Buckets->ExpectedCount[i] += Buckets->ExpectedCount[j]; } return Buckets; } // MakeBuckets /** * This routine computes the optimum number of histogram * buckets that should be used in a chi-squared goodness of * fit test for the specified number of samples. The optimum * number is computed based on Table 4.1 on pg. 147 of * "Measurement and Analysis of Random Data" by Bendat & Piersol. * Linear interpolation is used to interpolate between table * values. The table is intended for a 0.05 level of * significance (alpha). This routine assumes that it is * equally valid for other alpha's, which may not be true. * @param SampleCount number of samples to be tested * @return Optimum number of histogram buckets * @note Exceptions: None * @note History: 6/5/89, DSJ, Created. */ uinT16 OptimumNumberOfBuckets(uinT32 SampleCount) { uinT8 Last, Next; FLOAT32 Slope; if (SampleCount < kCountTable[0]) return kBucketsTable[0]; for (Last = 0, Next = 1; Next < LOOKUPTABLESIZE; Last++, Next++) { if (SampleCount <= kCountTable[Next]) { Slope = (FLOAT32) (kBucketsTable[Next] - kBucketsTable[Last]) / (FLOAT32) (kCountTable[Next] - kCountTable[Last]); return ((uinT16) (kBucketsTable[Last] + Slope * (SampleCount - kCountTable[Last]))); } } return kBucketsTable[Last]; } // OptimumNumberOfBuckets /** * This routine computes the chi-squared value which will * leave a cumulative probability of Alpha in the right tail * of a chi-squared distribution with the specified number of * degrees of freedom. Alpha must be between 0 and 1. * DegreesOfFreedom must be even. The routine maintains an * array of lists. Each list corresponds to a different * number of degrees of freedom. Each entry in the list * corresponds to a different alpha value and its corresponding * chi-squared value. Therefore, once a particular chi-squared * value is computed, it is stored in the list and never * needs to be computed again. * @param DegreesOfFreedom determines shape of distribution * @param Alpha probability of right tail * @return Desired chi-squared value * @note Exceptions: none * @note History: 6/5/89, DSJ, Created. */ FLOAT64 ComputeChiSquared (uinT16 DegreesOfFreedom, FLOAT64 Alpha) #define CHIACCURACY 0.01 #define MINALPHA (1e-200) { static LIST ChiWith[MAXDEGREESOFFREEDOM + 1]; CHISTRUCT *OldChiSquared; CHISTRUCT SearchKey; // limit the minimum alpha that can be used - if alpha is too small // it may not be possible to compute chi-squared. Alpha = ClipToRange(Alpha, MINALPHA, 1.0); if (Odd (DegreesOfFreedom)) DegreesOfFreedom++; /* find the list of chi-squared values which have already been computed for the specified number of degrees of freedom. Search the list for the desired chi-squared. */ SearchKey.Alpha = Alpha; OldChiSquared = (CHISTRUCT *) first_node (search (ChiWith[DegreesOfFreedom], &SearchKey, AlphaMatch)); if (OldChiSquared == NULL) { OldChiSquared = NewChiStruct (DegreesOfFreedom, Alpha); OldChiSquared->ChiSquared = Solve (ChiArea, OldChiSquared, (FLOAT64) DegreesOfFreedom, (FLOAT64) CHIACCURACY); ChiWith[DegreesOfFreedom] = push (ChiWith[DegreesOfFreedom], OldChiSquared); } else { // further optimization might move OldChiSquared to front of list } return (OldChiSquared->ChiSquared); } // ComputeChiSquared /** * This routine computes the probability density function * of a discrete normal distribution defined by the global * variables kNormalMean, kNormalVariance, and kNormalMagnitude. * Normal magnitude could, of course, be computed in terms of * the normal variance but it is precomputed for efficiency. * @param x number to compute the normal probability density for * @note Globals: * kNormalMean mean of a discrete normal distribution * kNormalVariance variance of a discrete normal distribution * kNormalMagnitude magnitude of a discrete normal distribution * @return The value of the normal distribution at x. * @note Exceptions: None * @note History: 6/4/89, DSJ, Created. */ FLOAT64 NormalDensity(inT32 x) { FLOAT64 Distance; Distance = x - kNormalMean; return kNormalMagnitude * exp(-0.5 * Distance * Distance / kNormalVariance); } // NormalDensity /** * This routine computes the probability density function * of a uniform distribution at the specified point. The * range of the distribution is from 0 to BUCKETTABLESIZE. * @param x number to compute the uniform probability density for * @return The value of the uniform distribution at x. * @note Exceptions: None * @note History: 6/5/89, DSJ, Created. */ FLOAT64 UniformDensity(inT32 x) { static FLOAT64 UniformDistributionDensity = (FLOAT64) 1.0 / BUCKETTABLESIZE; if ((x >= 0.0) && (x <= BUCKETTABLESIZE)) return UniformDistributionDensity; else return (FLOAT64) 0.0; } // UniformDensity /** * This routine computes a trapezoidal approximation to the * integral of a function over a small delta in x. * @param f1 value of function at x1 * @param f2 value of function at x2 * @param Dx x2 - x1 (should always be positive) * @return Approximation of the integral of the function from x1 to x2. * @note Exceptions: None * @note History: 6/5/89, DSJ, Created. */ FLOAT64 Integral(FLOAT64 f1, FLOAT64 f2, FLOAT64 Dx) { return (f1 + f2) * Dx / 2.0; } // Integral /** * This routine counts the number of cluster samples which * fall within the various histogram buckets in Buckets. Only * one dimension of each sample is examined. The exact meaning * of the Mean and StdDev parameters depends on the * distribution which is being analyzed (this info is in the * Buckets data structure). For normal distributions, Mean * and StdDev have the expected meanings. For uniform and * random distributions the Mean is the center point of the * range and the StdDev is 1/2 the range. A dimension with * zero standard deviation cannot be statistically analyzed. * In this case, a pseudo-analysis is used. * @param Buckets histogram buckets to count samples * @param Cluster cluster whose samples are being analyzed * @param Dim dimension of samples which is being analyzed * @param ParamDesc description of the dimension * @param Mean "mean" of the distribution * @param StdDev "standard deviation" of the distribution * @return None (the Buckets data structure is filled in) * @note Exceptions: None * @note History: 6/5/89, DSJ, Created. */ void FillBuckets(BUCKETS *Buckets, CLUSTER *Cluster, uinT16 Dim, PARAM_DESC *ParamDesc, FLOAT32 Mean, FLOAT32 StdDev) { uinT16 BucketID; int i; LIST SearchState; SAMPLE *Sample; // initialize the histogram bucket counts to 0 for (i = 0; i < Buckets->NumberOfBuckets; i++) Buckets->Count[i] = 0; if (StdDev == 0.0) { /* if the standard deviation is zero, then we can't statistically analyze the cluster. Use a pseudo-analysis: samples exactly on the mean are distributed evenly across all buckets. Samples greater than the mean are placed in the last bucket; samples less than the mean are placed in the first bucket. */ InitSampleSearch(SearchState, Cluster); i = 0; while ((Sample = NextSample (&SearchState)) != NULL) { if (Sample->Mean[Dim] > Mean) BucketID = Buckets->NumberOfBuckets - 1; else if (Sample->Mean[Dim] < Mean) BucketID = 0; else BucketID = i; Buckets->Count[BucketID] += 1; i++; if (i >= Buckets->NumberOfBuckets) i = 0; } } else { // search for all samples in the cluster and add to histogram buckets InitSampleSearch(SearchState, Cluster); while ((Sample = NextSample (&SearchState)) != NULL) { switch (Buckets->Distribution) { case normal: BucketID = NormalBucket (ParamDesc, Sample->Mean[Dim], Mean, StdDev); break; case D_random: case uniform: BucketID = UniformBucket (ParamDesc, Sample->Mean[Dim], Mean, StdDev); break; default: BucketID = 0; } Buckets->Count[Buckets->Bucket[BucketID]] += 1; } } } // FillBuckets /** * This routine determines which bucket x falls into in the * discrete normal distribution defined by kNormalMean * and kNormalStdDev. x values which exceed the range of * the discrete distribution are clipped. * @param ParamDesc used to identify circular dimensions * @param x value to be normalized * @param Mean mean of normal distribution * @param StdDev standard deviation of normal distribution * @return Bucket number into which x falls * @note Exceptions: None * @note History: 6/5/89, DSJ, Created. */ uinT16 NormalBucket(PARAM_DESC *ParamDesc, FLOAT32 x, FLOAT32 Mean, FLOAT32 StdDev) { FLOAT32 X; // wraparound circular parameters if necessary if (ParamDesc->Circular) { if (x - Mean > ParamDesc->HalfRange) x -= ParamDesc->Range; else if (x - Mean < -ParamDesc->HalfRange) x += ParamDesc->Range; } X = ((x - Mean) / StdDev) * kNormalStdDev + kNormalMean; if (X < 0) return 0; if (X > BUCKETTABLESIZE - 1) return ((uinT16) (BUCKETTABLESIZE - 1)); return (uinT16) floor((FLOAT64) X); } // NormalBucket /** * This routine determines which bucket x falls into in the * discrete uniform distribution defined by * BUCKETTABLESIZE. x values which exceed the range of * the discrete distribution are clipped. * @param ParamDesc used to identify circular dimensions * @param x value to be normalized * @param Mean center of range of uniform distribution * @param StdDev 1/2 the range of the uniform distribution * @return Bucket number into which x falls * @note Exceptions: None * @note History: 6/5/89, DSJ, Created. */ uinT16 UniformBucket(PARAM_DESC *ParamDesc, FLOAT32 x, FLOAT32 Mean, FLOAT32 StdDev) { FLOAT32 X; // wraparound circular parameters if necessary if (ParamDesc->Circular) { if (x - Mean > ParamDesc->HalfRange) x -= ParamDesc->Range; else if (x - Mean < -ParamDesc->HalfRange) x += ParamDesc->Range; } X = ((x - Mean) / (2 * StdDev) * BUCKETTABLESIZE + BUCKETTABLESIZE / 2.0); if (X < 0) return 0; if (X > BUCKETTABLESIZE - 1) return (uinT16) (BUCKETTABLESIZE - 1); return (uinT16) floor((FLOAT64) X); } // UniformBucket /** * This routine performs a chi-square goodness of fit test * on the histogram data in the Buckets data structure. TRUE * is returned if the histogram matches the probability * distribution which was specified when the Buckets * structure was originally created. Otherwise FALSE is * returned. * @param Buckets histogram data to perform chi-square test on * @return TRUE if samples match distribution, FALSE otherwise * @note Exceptions: None * @note History: 6/5/89, DSJ, Created. */ BOOL8 DistributionOK(BUCKETS *Buckets) { FLOAT32 FrequencyDifference; FLOAT32 TotalDifference; int i; // compute how well the histogram matches the expected histogram TotalDifference = 0.0; for (i = 0; i < Buckets->NumberOfBuckets; i++) { FrequencyDifference = Buckets->Count[i] - Buckets->ExpectedCount[i]; TotalDifference += (FrequencyDifference * FrequencyDifference) / Buckets->ExpectedCount[i]; } // test to see if the difference is more than expected if (TotalDifference > Buckets->ChiSquared) return FALSE; else return TRUE; } // DistributionOK /** * This routine frees the memory used by the statistics * data structure. * @param Statistics pointer to data structure to be freed * @return None * @note Exceptions: None * @note History: 6/5/89, DSJ, Created. */ void FreeStatistics(STATISTICS *Statistics) { memfree (Statistics->CoVariance); memfree (Statistics->Min); memfree (Statistics->Max); memfree(Statistics); } // FreeStatistics /** * This routine properly frees the memory used by a BUCKETS. * * @param buckets pointer to data structure to be freed */ void FreeBuckets(BUCKETS *buckets) { Efree(buckets->Count); Efree(buckets->ExpectedCount); Efree(buckets); } // FreeBuckets /** * This routine frees the memory consumed by the specified * cluster and all of its subclusters. This is done by * recursive calls to FreeCluster(). * * @param Cluster pointer to cluster to be freed * * @return None * * @note Exceptions: None * @note History: 6/6/89, DSJ, Created. */ void FreeCluster(CLUSTER *Cluster) { if (Cluster != NULL) { FreeCluster (Cluster->Left); FreeCluster (Cluster->Right); memfree(Cluster); } } // FreeCluster /** * This routine computes the degrees of freedom that should * be used in a chi-squared test with the specified number of * histogram buckets. The result is always rounded up to * the next even number so that the value of chi-squared can be * computed more easily. This will cause the value of * chi-squared to be higher than the optimum value, resulting * in the chi-square test being more lenient than optimum. * @param Distribution distribution being tested for * @param HistogramBuckets number of buckets in chi-square test * @return The number of degrees of freedom for a chi-square test * @note Exceptions: none * @note History: Thu Aug 3 14:04:18 1989, DSJ, Created. */ uinT16 DegreesOfFreedom(DISTRIBUTION Distribution, uinT16 HistogramBuckets) { static uinT8 DegreeOffsets[] = { 3, 3, 1 }; uinT16 AdjustedNumBuckets; AdjustedNumBuckets = HistogramBuckets - DegreeOffsets[(int) Distribution]; if (Odd (AdjustedNumBuckets)) AdjustedNumBuckets++; return (AdjustedNumBuckets); } // DegreesOfFreedom /** * This routine is used to search a list of histogram data * structures to find one with the specified number of * buckets. It is called by the list search routines. * @param arg1 current histogram being tested for a match * @param arg2 match key * @return TRUE if arg1 matches arg2 * @note Exceptions: none * @note History: Thu Aug 3 14:17:33 1989, DSJ, Created. */ int NumBucketsMatch(void *arg1, // BUCKETS *Histogram, void *arg2) { // uinT16 *DesiredNumberOfBuckets) BUCKETS *Histogram = (BUCKETS *) arg1; uinT16 *DesiredNumberOfBuckets = (uinT16 *) arg2; return (*DesiredNumberOfBuckets == Histogram->NumberOfBuckets); } // NumBucketsMatch /** * This routine is used to search a list for a list node * whose contents match Key. It is called by the list * delete_d routine. * @return TRUE if ListNode matches Key * @note Exceptions: none * @note History: Thu Aug 3 14:23:58 1989, DSJ, Created. */ int ListEntryMatch(void *arg1, //ListNode void *arg2) { //Key return (arg1 == arg2); } // ListEntryMatch /** * This routine multiplies each ExpectedCount histogram entry * by NewSampleCount/OldSampleCount so that the histogram * is now adjusted to the new sample count. * @param Buckets histogram data structure to adjust * @param NewSampleCount new sample count to adjust to * @return none * @note Exceptions: none * @note History: Thu Aug 3 14:31:14 1989, DSJ, Created. */ void AdjustBuckets(BUCKETS *Buckets, uinT32 NewSampleCount) { int i; FLOAT64 AdjustFactor; AdjustFactor = (((FLOAT64) NewSampleCount) / ((FLOAT64) Buckets->SampleCount)); for (i = 0; i < Buckets->NumberOfBuckets; i++) { Buckets->ExpectedCount[i] *= AdjustFactor; } Buckets->SampleCount = NewSampleCount; } // AdjustBuckets /** * This routine sets the bucket counts in the specified histogram * to zero. * @param Buckets histogram data structure to init * @return none * @note Exceptions: none * @note History: Thu Aug 3 14:31:14 1989, DSJ, Created. */ void InitBuckets(BUCKETS *Buckets) { int i; for (i = 0; i < Buckets->NumberOfBuckets; i++) { Buckets->Count[i] = 0; } } // InitBuckets /** * This routine is used to search a list of structures which * hold pre-computed chi-squared values for a chi-squared * value whose corresponding alpha field matches the alpha * field of SearchKey. * * It is called by the list search routines. * * @param arg1 chi-squared struct being tested for a match * @param arg2 chi-squared struct that is the search key * @return TRUE if ChiStruct's Alpha matches SearchKey's Alpha * @note Exceptions: none * @note History: Thu Aug 3 14:17:33 1989, DSJ, Created. */ int AlphaMatch(void *arg1, //CHISTRUCT *ChiStruct, void *arg2) { //CHISTRUCT *SearchKey) CHISTRUCT *ChiStruct = (CHISTRUCT *) arg1; CHISTRUCT *SearchKey = (CHISTRUCT *) arg2; return (ChiStruct->Alpha == SearchKey->Alpha); } // AlphaMatch /** * This routine allocates a new data structure which is used * to hold a chi-squared value along with its associated * number of degrees of freedom and alpha value. * * @param DegreesOfFreedom degrees of freedom for new chi value * @param Alpha confidence level for new chi value * @return none * @note Exceptions: none * @note History: Fri Aug 4 11:04:59 1989, DSJ, Created. */ CHISTRUCT *NewChiStruct(uinT16 DegreesOfFreedom, FLOAT64 Alpha) { CHISTRUCT *NewChiStruct; NewChiStruct = (CHISTRUCT *) Emalloc (sizeof (CHISTRUCT)); NewChiStruct->DegreesOfFreedom = DegreesOfFreedom; NewChiStruct->Alpha = Alpha; return (NewChiStruct); } // NewChiStruct /** * This routine attempts to find an x value at which Function * goes to zero (i.e. a root of the function ). It will only * work correctly if a solution actually exists and there * are no extrema between the solution and the InitialGuess. * The algorithms used are extremely primitive. * * @param Function function whose zero is to be found * @param FunctionParams arbitrary data to pass to function * @param InitialGuess point to start solution search at * @param Accuracy maximum allowed error * @return Solution of function ( x for which f(x) = 0 ). * @note Exceptions: none * @note History: Fri Aug 4 11:08:59 1989, DSJ, Created. */ FLOAT64 Solve (SOLVEFUNC Function, void *FunctionParams, FLOAT64 InitialGuess, FLOAT64 Accuracy) #define INITIALDELTA 0.1 #define DELTARATIO 0.1 { FLOAT64 x; FLOAT64 f; FLOAT64 Slope; FLOAT64 Delta; FLOAT64 NewDelta; FLOAT64 xDelta; FLOAT64 LastPosX, LastNegX; x = InitialGuess; Delta = INITIALDELTA; LastPosX = MAX_FLOAT32; LastNegX = -MAX_FLOAT32; f = (*Function) ((CHISTRUCT *) FunctionParams, x); while (Abs (LastPosX - LastNegX) > Accuracy) { // keep track of outer bounds of current estimate if (f < 0) LastNegX = x; else LastPosX = x; // compute the approx. slope of f(x) at the current point Slope = ((*Function) ((CHISTRUCT *) FunctionParams, x + Delta) - f) / Delta; // compute the next solution guess */ xDelta = f / Slope; x -= xDelta; // reduce the delta used for computing slope to be a fraction of //the amount moved to get to the new guess NewDelta = Abs (xDelta) * DELTARATIO; if (NewDelta < Delta) Delta = NewDelta; // compute the value of the function at the new guess f = (*Function) ((CHISTRUCT *) FunctionParams, x); } return (x); } // Solve /** * This routine computes the area under a chi density curve * from 0 to x, minus the desired area under the curve. The * number of degrees of freedom of the chi curve is specified * in the ChiParams structure. The desired area is also * specified in the ChiParams structure as Alpha ( or 1 minus * the desired area ). This routine is intended to be passed * to the Solve() function to find the value of chi-squared * which will yield a desired area under the right tail of * the chi density curve. The function will only work for * even degrees of freedom. The equations are based on * integrating the chi density curve in parts to obtain * a series that can be used to compute the area under the * curve. * @param ChiParams contains degrees of freedom and alpha * @param x value of chi-squared to evaluate * @return Error between actual and desired area under the chi curve. * @note Exceptions: none * @note History: Fri Aug 4 12:48:41 1989, DSJ, Created. */ FLOAT64 ChiArea(CHISTRUCT *ChiParams, FLOAT64 x) { int i, N; FLOAT64 SeriesTotal; FLOAT64 Denominator; FLOAT64 PowerOfx; N = ChiParams->DegreesOfFreedom / 2 - 1; SeriesTotal = 1; Denominator = 1; PowerOfx = 1; for (i = 1; i <= N; i++) { Denominator *= 2 * i; PowerOfx *= x; SeriesTotal += PowerOfx / Denominator; } return ((SeriesTotal * exp (-0.5 * x)) - ChiParams->Alpha); } // ChiArea /** * This routine looks at all samples in the specified cluster. * It computes a running estimate of the percentage of the * charaters which have more than 1 sample in the cluster. * When this percentage exceeds MaxIllegal, TRUE is returned. * Otherwise FALSE is returned. The CharID * fields must contain integers which identify the training * characters which were used to generate the sample. One * integer is used for each sample. The NumChar field in * the Clusterer must contain the number of characters in the * training set. All CharID fields must be between 0 and * NumChar-1. The main function of this routine is to help * identify clusters which need to be split further, i.e. if * numerous training characters have 2 or more features which are * contained in the same cluster, then the cluster should be * split. * * @param Clusterer data structure holding cluster tree * @param Cluster cluster containing samples to be tested * @param MaxIllegal max percentage of samples allowed to have * more than 1 feature in the cluster * @return TRUE if the cluster should be split, FALSE otherwise. * @note Exceptions: none * @note History: Wed Aug 30 11:13:05 1989, DSJ, Created. * 2/22/90, DSJ, Added MaxIllegal control rather than always * splitting illegal clusters. */ BOOL8 MultipleCharSamples (CLUSTERER * Clusterer, CLUSTER * Cluster, FLOAT32 MaxIllegal) #define ILLEGAL_CHAR 2 { static BOOL8 *CharFlags = NULL; static inT32 NumFlags = 0; int i; LIST SearchState; SAMPLE *Sample; inT32 CharID; inT32 NumCharInCluster; inT32 NumIllegalInCluster; FLOAT32 PercentIllegal; // initial estimate assumes that no illegal chars exist in the cluster NumCharInCluster = Cluster->SampleCount; NumIllegalInCluster = 0; if (Clusterer->NumChar > NumFlags) { if (CharFlags != NULL) memfree(CharFlags); NumFlags = Clusterer->NumChar; CharFlags = (BOOL8 *) Emalloc (NumFlags * sizeof (BOOL8)); } for (i = 0; i < NumFlags; i++) CharFlags[i] = FALSE; // find each sample in the cluster and check if we have seen it before InitSampleSearch(SearchState, Cluster); while ((Sample = NextSample (&SearchState)) != NULL) { CharID = Sample->CharID; if (CharFlags[CharID] == FALSE) { CharFlags[CharID] = TRUE; } else { if (CharFlags[CharID] == TRUE) { NumIllegalInCluster++; CharFlags[CharID] = ILLEGAL_CHAR; } NumCharInCluster--; PercentIllegal = (FLOAT32) NumIllegalInCluster / NumCharInCluster; if (PercentIllegal > MaxIllegal) { destroy(SearchState); return (TRUE); } } } return (FALSE); } // MultipleCharSamples /** * Compute the inverse of a matrix using LU decomposition with partial pivoting. * The return value is the sum of norms of the off-diagonal terms of the * product of a and inv. (A measure of the error.) */ double InvertMatrix(const float* input, int size, float* inv) { // Allocate memory for the 2D arrays. GENERIC_2D_ARRAY U(size, size, 0.0); GENERIC_2D_ARRAY U_inv(size, size, 0.0); GENERIC_2D_ARRAY L(size, size, 0.0); // Initialize the working matrices. U starts as input, L as I and U_inv as O. int row; int col; for (row = 0; row < size; row++) { for (col = 0; col < size; col++) { U[row][col] = input[row*size + col]; L[row][col] = row == col ? 1.0 : 0.0; U_inv[row][col] = 0.0; } } // Compute forward matrix by inversion by LU decomposition of input. for (col = 0; col < size; ++col) { // Find best pivot int best_row = 0; double best_pivot = -1.0; for (row = col; row < size; ++row) { if (Abs(U[row][col]) > best_pivot) { best_pivot = Abs(U[row][col]); best_row = row; } } // Exchange pivot rows. if (best_row != col) { for (int k = 0; k < size; ++k) { double tmp = U[best_row][k]; U[best_row][k] = U[col][k]; U[col][k] = tmp; tmp = L[best_row][k]; L[best_row][k] = L[col][k]; L[col][k] = tmp; } } // Now do the pivot itself. for (row = col + 1; row < size; ++row) { double ratio = -U[row][col] / U[col][col]; for (int j = col; j < size; ++j) { U[row][j] += U[col][j] * ratio; } for (int k = 0; k < size; ++k) { L[row][k] += L[col][k] * ratio; } } } // Next invert U. for (col = 0; col < size; ++col) { U_inv[col][col] = 1.0 / U[col][col]; for (row = col - 1; row >= 0; --row) { double total = 0.0; for (int k = col; k > row; --k) { total += U[row][k] * U_inv[k][col]; } U_inv[row][col] = -total / U[row][row]; } } // Now the answer is U_inv.L. for (row = 0; row < size; row++) { for (col = 0; col < size; col++) { double sum = 0.0; for (int k = row; k < size; ++k) { sum += U_inv[row][k] * L[k][col]; } inv[row*size + col] = sum; } } // Check matrix product. double error_sum = 0.0; for (row = 0; row < size; row++) { for (col = 0; col < size; col++) { double sum = 0.0; for (int k = 0; k < size; ++k) { sum += input[row*size + k] * inv[k *size + col]; } if (row != col) { error_sum += Abs(sum); } } } return error_sum; } tesseract-3.04.01/classify/cluster.h000066400000000000000000000132321266071204500173270ustar00rootroot00000000000000/****************************************************************************** ** Filename: cluster.h ** Purpose: Definition of feature space clustering routines ** Author: Dan Johnson ** History: 5/29/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 CLUSTER_H #define CLUSTER_H #include "kdtree.h" #include "oldlist.h" struct BUCKETS; #define MINBUCKETS 5 #define MAXBUCKETS 39 /*---------------------------------------------------------------------- Types ----------------------------------------------------------------------*/ typedef struct sample { unsigned Clustered:1; // TRUE if included in a higher cluster unsigned Prototype:1; // TRUE if cluster represented by a proto unsigned SampleCount:30; // number of samples in this cluster struct sample *Left; // ptr to left sub-cluster struct sample *Right; // ptr to right sub-cluster inT32 CharID; // identifier of char sample came from FLOAT32 Mean[1]; // mean of cluster - SampleSize floats } CLUSTER; typedef CLUSTER SAMPLE; // can refer to as either sample or cluster typedef enum { spherical, elliptical, mixed, automatic } PROTOSTYLE; typedef struct { // parameters to control clustering PROTOSTYLE ProtoStyle; // specifies types of protos to be made FLOAT32 MinSamples; // min # of samples per proto - % of total FLOAT32 MaxIllegal; // max percentage of samples in a cluster which have // more than 1 feature in that cluster FLOAT32 Independence; // desired independence between dimensions FLOAT64 Confidence; // desired confidence in prototypes created int MagicSamples; // Ideal number of samples in a cluster. } CLUSTERCONFIG; typedef enum { normal, uniform, D_random, DISTRIBUTION_COUNT } DISTRIBUTION; typedef union { FLOAT32 Spherical; FLOAT32 *Elliptical; } FLOATUNION; typedef struct { unsigned Significant:1; // TRUE if prototype is significant unsigned Merged:1; // Merged after clustering so do not output // but kept for display purposes. If it has no // samples then it was actually merged. // Otherwise it matched an already significant // cluster. unsigned Style:2; // spherical, elliptical, or mixed unsigned NumSamples:28; // number of samples in the cluster CLUSTER *Cluster; // ptr to cluster which made prototype DISTRIBUTION *Distrib; // different distribution for each dimension FLOAT32 *Mean; // prototype mean FLOAT32 TotalMagnitude; // total magnitude over all dimensions FLOAT32 LogMagnitude; // log base e of TotalMagnitude FLOATUNION Variance; // prototype variance FLOATUNION Magnitude; // magnitude of density function FLOATUNION Weight; // weight of density function } PROTOTYPE; typedef struct { inT16 SampleSize; // number of parameters per sample PARAM_DESC *ParamDesc; // description of each parameter inT32 NumberOfSamples; // total number of samples being clustered KDTREE *KDTree; // for optimal nearest neighbor searching CLUSTER *Root; // ptr to root cluster of cluster tree LIST ProtoList; // list of prototypes inT32 NumChar; // # of characters represented by samples // cache of reusable histograms by distribution type and number of buckets. BUCKETS* bucket_cache[DISTRIBUTION_COUNT][MAXBUCKETS + 1 - MINBUCKETS]; } CLUSTERER; typedef struct { inT32 NumSamples; // number of samples in list inT32 MaxNumSamples; // maximum size of list SAMPLE *Sample[1]; // array of ptrs to sample data structures } SAMPLELIST; // low level cluster tree analysis routines. #define InitSampleSearch(S,C) (((C)==NULL)?(S=NIL_LIST):(S=push(NIL_LIST,(C)))) /*-------------------------------------------------------------------------- Public Function Prototypes --------------------------------------------------------------------------*/ CLUSTERER *MakeClusterer (inT16 SampleSize, const PARAM_DESC ParamDesc[]); SAMPLE *MakeSample(CLUSTERER * Clusterer, const FLOAT32* Feature, inT32 CharID); LIST ClusterSamples(CLUSTERER *Clusterer, CLUSTERCONFIG *Config); void FreeClusterer(CLUSTERER *Clusterer); void FreeProtoList(LIST *ProtoList); void FreePrototype(void *arg); // PROTOTYPE *Prototype); CLUSTER *NextSample(LIST *SearchState); FLOAT32 Mean(PROTOTYPE *Proto, uinT16 Dimension); FLOAT32 StandardDeviation(PROTOTYPE *Proto, uinT16 Dimension); inT32 MergeClusters(inT16 N, PARAM_DESC ParamDesc[], inT32 n1, inT32 n2, FLOAT32 m[], FLOAT32 m1[], FLOAT32 m2[]); //--------------Global Data Definitions and Declarations--------------------------- // define errors that can be trapped #define ALREADYCLUSTERED 4000 #endif tesseract-3.04.01/classify/clusttool.cpp000066400000000000000000000352731266071204500202420ustar00rootroot00000000000000/****************************************************************************** ** Filename: clustertool.c ** Purpose: Misc. tools for use with the clustering routines ** Author: Dan Johnson ** History: 6/6/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---------------------------------- #include "clusttool.h" #include "const.h" #include "danerror.h" #include "emalloc.h" #include "scanutils.h" #include #include //---------------Global Data Definitions and Declarations-------------------- #define TOKENSIZE 80 //< max size of tokens read from an input file #define MAXSAMPLESIZE 65535 //< max num of dimensions in feature space //#define MAXBLOCKSIZE 65535 //< max num of samples in a character (block size) /** * This routine reads a single integer from the specified * file and checks to ensure that it is between 0 and * MAXSAMPLESIZE. * @param File open text file to read sample size from * @return Sample size * @note Globals: None * @note Exceptions: ILLEGALSAMPLESIZE illegal format or range * @note History: 6/6/89, DSJ, Created. */ uinT16 ReadSampleSize(FILE *File) { int SampleSize; if ((tfscanf(File, "%d", &SampleSize) != 1) || (SampleSize < 0) || (SampleSize > MAXSAMPLESIZE)) DoError (ILLEGALSAMPLESIZE, "Illegal sample size"); return (SampleSize); } /** * This routine reads textual descriptions of sets of parameters * which describe the characteristics of feature dimensions. * * Exceptions: * - ILLEGALCIRCULARSPEC * - ILLEGALESSENTIALSPEC * - ILLEGALMINMAXSPEC * @param File open text file to read N parameter descriptions from * @param N number of parameter descriptions to read * @return Pointer to an array of parameter descriptors. * @note Globals: None * @note History: 6/6/89, DSJ, Created. */ PARAM_DESC *ReadParamDesc(FILE *File, uinT16 N) { int i; PARAM_DESC *ParamDesc; char Token[TOKENSIZE]; ParamDesc = (PARAM_DESC *) Emalloc (N * sizeof (PARAM_DESC)); for (i = 0; i < N; i++) { if (tfscanf(File, "%s", Token) != 1) DoError (ILLEGALCIRCULARSPEC, "Illegal circular/linear specification"); if (Token[0] == 'c') ParamDesc[i].Circular = TRUE; else ParamDesc[i].Circular = FALSE; if (tfscanf(File, "%s", Token) != 1) DoError (ILLEGALESSENTIALSPEC, "Illegal essential/non-essential spec"); if (Token[0] == 'e') ParamDesc[i].NonEssential = FALSE; else ParamDesc[i].NonEssential = TRUE; if (tfscanf(File, "%f%f", &(ParamDesc[i].Min), &(ParamDesc[i].Max)) != 2) DoError (ILLEGALMINMAXSPEC, "Illegal min or max specification"); ParamDesc[i].Range = ParamDesc[i].Max - ParamDesc[i].Min; ParamDesc[i].HalfRange = ParamDesc[i].Range / 2; ParamDesc[i].MidRange = (ParamDesc[i].Max + ParamDesc[i].Min) / 2; } return (ParamDesc); } /** * This routine reads a textual description of a prototype from * the specified file. * * Exceptions: * - ILLEGALSIGNIFICANCESPEC * - ILLEGALSAMPLECOUNT * - ILLEGALMEANSPEC * - ILLEGALVARIANCESPEC * - ILLEGALDISTRIBUTION * @param File open text file to read prototype from * @param N number of dimensions used in prototype * @return List of prototypes * @note Globals: None * @note History: 6/6/89, DSJ, Created. */ PROTOTYPE *ReadPrototype(FILE *File, uinT16 N) { char Token[TOKENSIZE]; int Status; PROTOTYPE *Proto; int SampleCount; int i; if ((Status = tfscanf(File, "%s", Token)) == 1) { Proto = (PROTOTYPE *) Emalloc (sizeof (PROTOTYPE)); Proto->Cluster = NULL; if (Token[0] == 's') Proto->Significant = TRUE; else Proto->Significant = FALSE; Proto->Style = ReadProtoStyle (File); if ((tfscanf(File, "%d", &SampleCount) != 1) || (SampleCount < 0)) DoError (ILLEGALSAMPLECOUNT, "Illegal sample count"); Proto->NumSamples = SampleCount; Proto->Mean = ReadNFloats (File, N, NULL); if (Proto->Mean == NULL) DoError (ILLEGALMEANSPEC, "Illegal prototype mean"); switch (Proto->Style) { case spherical: if (ReadNFloats (File, 1, &(Proto->Variance.Spherical)) == NULL) DoError (ILLEGALVARIANCESPEC, "Illegal prototype variance"); Proto->Magnitude.Spherical = 1.0 / sqrt ((double) (2.0 * PI * Proto->Variance.Spherical)); Proto->TotalMagnitude = pow (Proto->Magnitude.Spherical, (float) N); Proto->LogMagnitude = log ((double) Proto->TotalMagnitude); Proto->Weight.Spherical = 1.0 / Proto->Variance.Spherical; Proto->Distrib = NULL; break; case elliptical: Proto->Variance.Elliptical = ReadNFloats (File, N, NULL); if (Proto->Variance.Elliptical == NULL) DoError (ILLEGALVARIANCESPEC, "Illegal prototype variance"); Proto->Magnitude.Elliptical = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); Proto->Weight.Elliptical = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); Proto->TotalMagnitude = 1.0; for (i = 0; i < N; i++) { Proto->Magnitude.Elliptical[i] = 1.0 / sqrt ((double) (2.0 * PI * Proto->Variance.Elliptical[i])); Proto->Weight.Elliptical[i] = 1.0 / Proto->Variance.Elliptical[i]; Proto->TotalMagnitude *= Proto->Magnitude.Elliptical[i]; } Proto->LogMagnitude = log ((double) Proto->TotalMagnitude); Proto->Distrib = NULL; break; case mixed: Proto->Distrib = (DISTRIBUTION *) Emalloc (N * sizeof (DISTRIBUTION)); for (i = 0; i < N; i++) { if (tfscanf(File, "%s", Token) != 1) DoError (ILLEGALDISTRIBUTION, "Illegal prototype distribution"); switch (Token[0]) { case 'n': Proto->Distrib[i] = normal; break; case 'u': Proto->Distrib[i] = uniform; break; case 'r': Proto->Distrib[i] = D_random; break; default: DoError (ILLEGALDISTRIBUTION, "Illegal prototype distribution"); } } Proto->Variance.Elliptical = ReadNFloats (File, N, NULL); if (Proto->Variance.Elliptical == NULL) DoError (ILLEGALVARIANCESPEC, "Illegal prototype variance"); Proto->Magnitude.Elliptical = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); Proto->Weight.Elliptical = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); Proto->TotalMagnitude = 1.0; for (i = 0; i < N; i++) { switch (Proto->Distrib[i]) { case normal: Proto->Magnitude.Elliptical[i] = 1.0 / sqrt ((double) (2.0 * PI * Proto->Variance.Elliptical[i])); Proto->Weight.Elliptical[i] = 1.0 / Proto->Variance.Elliptical[i]; break; case uniform: case D_random: Proto->Magnitude.Elliptical[i] = 1.0 / (2.0 * Proto->Variance.Elliptical[i]); break; case DISTRIBUTION_COUNT: ASSERT_HOST(!"Distribution count not allowed!"); } Proto->TotalMagnitude *= Proto->Magnitude.Elliptical[i]; } Proto->LogMagnitude = log ((double) Proto->TotalMagnitude); break; } return (Proto); } else if (Status == EOF) return (NULL); else { DoError (ILLEGALSIGNIFICANCESPEC, "Illegal significance specification"); return (NULL); } } /** * This routine reads an single token from the specified * text file and interprets it as a prototype specification. * @param File open text file to read prototype style from * @return Prototype style read from text file * @note Globals: None * @note Exceptions: ILLEGALSTYLESPEC illegal prototype style specification * @note History: 6/8/89, DSJ, Created. */ PROTOSTYLE ReadProtoStyle(FILE *File) { char Token[TOKENSIZE]; PROTOSTYLE Style; if (tfscanf(File, "%s", Token) != 1) DoError (ILLEGALSTYLESPEC, "Illegal prototype style specification"); switch (Token[0]) { case 's': Style = spherical; break; case 'e': Style = elliptical; break; case 'm': Style = mixed; break; case 'a': Style = automatic; break; default: Style = elliptical; DoError (ILLEGALSTYLESPEC, "Illegal prototype style specification"); } return (Style); } /** * This routine reads N floats from the specified text file * and places them into Buffer. If Buffer is NULL, a buffer * is created and passed back to the caller. If EOF is * encountered before any floats can be read, NULL is * returned. * @param File open text file to read floats from * @param N number of floats to read * @param Buffer pointer to buffer to place floats into * @return Pointer to buffer holding floats or NULL if EOF * @note Globals: None * @note Exceptions: ILLEGALFLOAT * @note History: 6/6/89, DSJ, Created. */ FLOAT32* ReadNFloats(FILE * File, uinT16 N, FLOAT32 Buffer[]) { bool needs_free = false; int i; int NumFloatsRead; if (Buffer == NULL) { Buffer = reinterpret_cast(Emalloc(N * sizeof(FLOAT32))); needs_free = true; } for (i = 0; i < N; i++) { NumFloatsRead = tfscanf(File, "%f", &(Buffer[i])); if (NumFloatsRead != 1) { if ((NumFloatsRead == EOF) && (i == 0)) { if (needs_free) { Efree(Buffer); } return NULL; } else { DoError(ILLEGALFLOAT, "Illegal float specification"); } } } return Buffer; } /** * This routine writes an array of dimension descriptors to * the specified text file. * @param File open text file to write param descriptors to * @param N number of param descriptors to write * @param ParamDesc array of param descriptors to write * @return None * @note Globals: None * @note Exceptions: None * @note History: 6/6/89, DSJ, Created. */ void WriteParamDesc (FILE * File, uinT16 N, PARAM_DESC ParamDesc[]) { int i; for (i = 0; i < N; i++) { if (ParamDesc[i].Circular) fprintf (File, "circular "); else fprintf (File, "linear "); if (ParamDesc[i].NonEssential) fprintf (File, "non-essential "); else fprintf (File, "essential "); fprintf (File, "%10.6f %10.6f\n", ParamDesc[i].Min, ParamDesc[i].Max); } } /** * This routine writes a textual description of a prototype * to the specified text file. * @param File open text file to write prototype to * @param N number of dimensions in feature space * @param Proto prototype to write out * @return None * @note Globals: None * @note Exceptions: None * @note History: 6/12/89, DSJ, Created. */ void WritePrototype(FILE *File, uinT16 N, PROTOTYPE *Proto) { int i; if (Proto->Significant) fprintf (File, "significant "); else fprintf (File, "insignificant "); WriteProtoStyle (File, (PROTOSTYLE) Proto->Style); fprintf (File, "%6d\n\t", Proto->NumSamples); WriteNFloats (File, N, Proto->Mean); fprintf (File, "\t"); switch (Proto->Style) { case spherical: WriteNFloats (File, 1, &(Proto->Variance.Spherical)); break; case elliptical: WriteNFloats (File, N, Proto->Variance.Elliptical); break; case mixed: for (i = 0; i < N; i++) switch (Proto->Distrib[i]) { case normal: fprintf (File, " %9s", "normal"); break; case uniform: fprintf (File, " %9s", "uniform"); break; case D_random: fprintf (File, " %9s", "random"); break; case DISTRIBUTION_COUNT: ASSERT_HOST(!"Distribution count not allowed!"); } fprintf (File, "\n\t"); WriteNFloats (File, N, Proto->Variance.Elliptical); } } /** * This routine writes a text representation of N floats from * an array to a file. All of the floats are placed on one line. * @param File open text file to write N floats to * @param N number of floats to write * @param Array array of floats to write * @return None * @note Globals: None * @note Exceptions: None * @note History: 6/6/89, DSJ, Created. */ void WriteNFloats(FILE * File, uinT16 N, FLOAT32 Array[]) { for (int i = 0; i < N; i++) fprintf(File, " %9.6f", Array[i]); fprintf(File, "\n"); } /** * This routine writes to the specified text file a word * which represents the ProtoStyle. It does not append * a carriage return to the end. * @param File open text file to write prototype style to * @param ProtoStyle prototype style to write * @return None * @note Globals: None * @note Exceptions: None * @note History: 6/8/89, DSJ, Created. */ void WriteProtoStyle(FILE *File, PROTOSTYLE ProtoStyle) { switch (ProtoStyle) { case spherical: fprintf (File, "spherical"); break; case elliptical: fprintf (File, "elliptical"); break; case mixed: fprintf (File, "mixed"); break; case automatic: fprintf (File, "automatic"); break; } } /** * This routine writes a textual description of each prototype * in the prototype list to the specified file. It also * writes a file header which includes the number of dimensions * in feature space and the descriptions for each dimension. * @param File open text file to write prototypes to * @param N number of dimensions in feature space * @param ParamDesc descriptions for each dimension * @param ProtoList list of prototypes to be written * @param WriteSigProtos TRUE to write out significant prototypes * @param WriteInsigProtos TRUE to write out insignificants * @note Globals: None * @return None * @note Exceptions: None * @note History: 6/12/89, DSJ, Created. */ void WriteProtoList( FILE *File, uinT16 N, PARAM_DESC ParamDesc[], LIST ProtoList, BOOL8 WriteSigProtos, BOOL8 WriteInsigProtos) { PROTOTYPE *Proto; /* write file header */ fprintf(File,"%0d\n",N); WriteParamDesc(File,N,ParamDesc); /* write prototypes */ iterate(ProtoList) { Proto = (PROTOTYPE *) first_node ( ProtoList ); if (( Proto->Significant && WriteSigProtos ) || ( ! Proto->Significant && WriteInsigProtos ) ) WritePrototype( File, N, Proto ); } } tesseract-3.04.01/classify/clusttool.h000066400000000000000000000045551266071204500177060ustar00rootroot00000000000000/****************************************************************************** ** Filename: clusttool.h ** Purpose: Definition of clustering utility tools ** Author: Dan Johnson ** History: 6/6/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 __CLUSTERTOOL__ #define __CLUSTERTOOL__ //--------------------------Include Files--------------------------------------- #include "host.h" #include "cluster.h" #include /*------------------------------------------------------------------------- Public Function Prototype --------------------------------------------------------------------------*/ uinT16 ReadSampleSize(FILE *File); PARAM_DESC *ReadParamDesc(FILE *File, uinT16 N); PROTOTYPE *ReadPrototype(FILE *File, uinT16 N); PROTOSTYLE ReadProtoStyle(FILE *File); FLOAT32 *ReadNFloats (FILE * File, uinT16 N, FLOAT32 Buffer[]); void WriteParamDesc (FILE * File, uinT16 N, PARAM_DESC ParamDesc[]); void WritePrototype(FILE *File, uinT16 N, PROTOTYPE *Proto); void WriteNFloats (FILE * File, uinT16 N, FLOAT32 Array[]); void WriteProtoStyle(FILE *File, PROTOSTYLE ProtoStyle); void WriteProtoList( FILE *File, uinT16 N, PARAM_DESC ParamDesc[], LIST ProtoList, BOOL8 WriteSigProtos, BOOL8 WriteInsigProtos); //--------------Global Data Definitions and Declarations--------------------- // define errors that can be trapped #define ILLEGALSAMPLESIZE 5000 #define ILLEGALCIRCULARSPEC 5001 #define ILLEGALMINMAXSPEC 5002 #define ILLEGALSIGNIFICANCESPEC 5003 #define ILLEGALSTYLESPEC 5004 #define ILLEGALSAMPLECOUNT 5005 #define ILLEGALMEANSPEC 5006 #define ILLEGALVARIANCESPEC 5007 #define ILLEGALDISTRIBUTION 5008 #define ILLEGALFLOAT 5009 #define ILLEGALESSENTIALSPEC 5013 #endif tesseract-3.04.01/classify/cutoffs.cpp000066400000000000000000000053601266071204500176550ustar00rootroot00000000000000/****************************************************************************** ** Filename: cutoffs.c ** Purpose: Routines to manipulate an array of class cutoffs. ** Author: Dan Johnson ** History: Wed Feb 20 09:28:51 1991, 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 "cutoffs.h" #include #include "classify.h" #include "efio.h" #include "globals.h" #include "helpers.h" #include "scanutils.h" #include "serialis.h" #include "unichar.h" #define REALLY_QUOTE_IT(x) QUOTE_IT(x) #define MAX_CUTOFF 1000 namespace tesseract { /** * Open Filename, read in all of the class-id/cutoff pairs * and insert them into the Cutoffs array. Cutoffs are * indexed in the array by class id. Unused entries in the * array are set to an arbitrarily high cutoff value. * @param CutoffFile name of file containing cutoff definitions * @param Cutoffs array to put cutoffs into * @param swap * @param end_offset * @return none * @note Globals: none * @note Exceptions: none * @note History: Wed Feb 20 09:38:26 1991, DSJ, Created. */ void Classify::ReadNewCutoffs(FILE *CutoffFile, bool swap, inT64 end_offset, CLASS_CUTOFF_ARRAY Cutoffs) { char Class[UNICHAR_LEN + 1]; CLASS_ID ClassId; int Cutoff; int i; if (shape_table_ != NULL) { if (!shapetable_cutoffs_.DeSerialize(swap, CutoffFile)) { tprintf("Error during read of shapetable pffmtable!\n"); } } for (i = 0; i < MAX_NUM_CLASSES; i++) Cutoffs[i] = MAX_CUTOFF; while ((end_offset < 0 || ftell(CutoffFile) < end_offset) && tfscanf(CutoffFile, "%" REALLY_QUOTE_IT(UNICHAR_LEN) "s %d", Class, &Cutoff) == 2) { if (strcmp(Class, "NULL") == 0) { ClassId = unicharset.unichar_to_id(" "); } else { ClassId = unicharset.unichar_to_id(Class); } Cutoffs[ClassId] = Cutoff; SkipNewline(CutoffFile); } } } // namespace tesseract tesseract-3.04.01/classify/cutoffs.h000066400000000000000000000032201266071204500173130ustar00rootroot00000000000000/****************************************************************************** ** Filename: cutoffs.h ** Purpose: Routines to manipulate an array of class cutoffs. ** Author: Dan Johnson ** History: Wed Feb 20 09:34:20 1991, 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 CUTOFFS_H #define CUTOFFS_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "matchdefs.h" typedef uinT16 CLASS_CUTOFF_ARRAY[MAX_NUM_CLASSES]; /**---------------------------------------------------------------------------- Public Function Prototypes ----------------------------------------------------------------------------**/ /* #if defined(__STDC__) || defined(__cplusplus) # define _ARGS(s) s #else # define _ARGS(s) () #endif*/ /* cutoffs.c void ReadNewCutoffs _ARGS((char *Filename, CLASS_CUTOFF_ARRAY Cutoffs)); #undef _ARGS */ #endif tesseract-3.04.01/classify/errorcounter.cpp000066400000000000000000000512571266071204500207430ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) // // 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 "errorcounter.h" #include "fontinfo.h" #include "ndminx.h" #include "sampleiterator.h" #include "shapeclassifier.h" #include "shapetable.h" #include "trainingsample.h" #include "trainingsampleset.h" #include "unicity_table.h" namespace tesseract { // Difference in result rating to be thought of as an "equal" choice. const double kRatingEpsilon = 1.0 / 32; // Tests a classifier, computing its error rate. // See errorcounter.h for description of arguments. // Iterates over the samples, calling the classifier in normal/silent mode. // If the classifier makes a CT_UNICHAR_TOPN_ERR error, and the appropriate // report_level is set (4 or greater), it will then call the classifier again // with a debug flag and a keep_this argument to find out what is going on. double ErrorCounter::ComputeErrorRate(ShapeClassifier* classifier, int report_level, CountTypes boosting_mode, const FontInfoTable& fontinfo_table, const GenericVector& page_images, SampleIterator* it, double* unichar_error, double* scaled_error, STRING* fonts_report) { int fontsize = it->sample_set()->NumFonts(); ErrorCounter counter(classifier->GetUnicharset(), fontsize); GenericVector results; clock_t start = clock(); int total_samples = 0; double unscaled_error = 0.0; // Set a number of samples on which to run the classify debug mode. int error_samples = report_level > 3 ? report_level * report_level : 0; // Iterate over all the samples, accumulating errors. for (it->Begin(); !it->AtEnd(); it->Next()) { TrainingSample* mutable_sample = it->MutableSample(); int page_index = mutable_sample->page_num(); Pix* page_pix = 0 <= page_index && page_index < page_images.size() ? page_images[page_index] : NULL; // No debug, no keep this. classifier->UnicharClassifySample(*mutable_sample, page_pix, 0, INVALID_UNICHAR_ID, &results); bool debug_it = false; int correct_id = mutable_sample->class_id(); if (counter.unicharset_.has_special_codes() && (correct_id == UNICHAR_SPACE || correct_id == UNICHAR_JOINED || correct_id == UNICHAR_BROKEN)) { // This is junk so use the special counter. debug_it = counter.AccumulateJunk(report_level > 3, results, mutable_sample); } else { debug_it = counter.AccumulateErrors(report_level > 3, boosting_mode, fontinfo_table, results, mutable_sample); } if (debug_it && error_samples > 0) { // Running debug, keep the correct answer, and debug the classifier. tprintf("Error on sample %d: %s Classifier debug output:\n", it->GlobalSampleIndex(), it->sample_set()->SampleToString(*mutable_sample).string()); classifier->DebugDisplay(*mutable_sample, page_pix, correct_id); --error_samples; } ++total_samples; } double total_time = 1.0 * (clock() - start) / CLOCKS_PER_SEC; // Create the appropriate error report. unscaled_error = counter.ReportErrors(report_level, boosting_mode, fontinfo_table, *it, unichar_error, fonts_report); if (scaled_error != NULL) *scaled_error = counter.scaled_error_; if (report_level > 1) { // It is useful to know the time in microseconds/char. tprintf("Errors computed in %.2fs at %.1f μs/char\n", total_time, 1000000.0 * total_time / total_samples); } return unscaled_error; } // Tests a pair of classifiers, debugging errors of the new against the old. // See errorcounter.h for description of arguments. // Iterates over the samples, calling the classifiers in normal/silent mode. // If the new_classifier makes a boosting_mode error that the old_classifier // does not, it will then call the new_classifier again with a debug flag // and a keep_this argument to find out what is going on. void ErrorCounter::DebugNewErrors( ShapeClassifier* new_classifier, ShapeClassifier* old_classifier, CountTypes boosting_mode, const FontInfoTable& fontinfo_table, const GenericVector& page_images, SampleIterator* it) { int fontsize = it->sample_set()->NumFonts(); ErrorCounter old_counter(old_classifier->GetUnicharset(), fontsize); ErrorCounter new_counter(new_classifier->GetUnicharset(), fontsize); GenericVector results; int total_samples = 0; int error_samples = 25; int total_new_errors = 0; // Iterate over all the samples, accumulating errors. for (it->Begin(); !it->AtEnd(); it->Next()) { TrainingSample* mutable_sample = it->MutableSample(); int page_index = mutable_sample->page_num(); Pix* page_pix = 0 <= page_index && page_index < page_images.size() ? page_images[page_index] : NULL; // No debug, no keep this. old_classifier->UnicharClassifySample(*mutable_sample, page_pix, 0, INVALID_UNICHAR_ID, &results); int correct_id = mutable_sample->class_id(); if (correct_id != 0 && !old_counter.AccumulateErrors(true, boosting_mode, fontinfo_table, results, mutable_sample)) { // old classifier was correct, check the new one. new_classifier->UnicharClassifySample(*mutable_sample, page_pix, 0, INVALID_UNICHAR_ID, &results); if (correct_id != 0 && new_counter.AccumulateErrors(true, boosting_mode, fontinfo_table, results, mutable_sample)) { tprintf("New Error on sample %d: Classifier debug output:\n", it->GlobalSampleIndex()); ++total_new_errors; new_classifier->UnicharClassifySample(*mutable_sample, page_pix, 1, correct_id, &results); if (results.size() > 0 && error_samples > 0) { new_classifier->DebugDisplay(*mutable_sample, page_pix, correct_id); --error_samples; } } } ++total_samples; } tprintf("Total new errors = %d\n", total_new_errors); } // Constructor is private. Only anticipated use of ErrorCounter is via // the static ComputeErrorRate. ErrorCounter::ErrorCounter(const UNICHARSET& unicharset, int fontsize) : scaled_error_(0.0), rating_epsilon_(kRatingEpsilon), unichar_counts_(unicharset.size(), unicharset.size(), 0), ok_score_hist_(0, 101), bad_score_hist_(0, 101), unicharset_(unicharset) { Counts empty_counts; font_counts_.init_to_size(fontsize, empty_counts); multi_unichar_counts_.init_to_size(unicharset.size(), 0); } ErrorCounter::~ErrorCounter() { } // Accumulates the errors from the classifier results on a single sample. // Returns true if debug is true and a CT_UNICHAR_TOPN_ERR error occurred. // boosting_mode selects the type of error to be used for boosting and the // is_error_ member of sample is set according to whether the required type // of error occurred. The font_table provides access to font properties // for error counting and shape_table is used to understand the relationship // between unichar_ids and shape_ids in the results bool ErrorCounter::AccumulateErrors(bool debug, CountTypes boosting_mode, const FontInfoTable& font_table, const GenericVector& results, TrainingSample* sample) { int num_results = results.size(); int answer_actual_rank = -1; int font_id = sample->font_id(); int unichar_id = sample->class_id(); sample->set_is_error(false); if (num_results == 0) { // Reject. We count rejects as a separate category, but still mark the // sample as an error in case any training module wants to use that to // improve the classifier. sample->set_is_error(true); ++font_counts_[font_id].n[CT_REJECT]; } else { // Find rank of correct unichar answer, using rating_epsilon_ to allow // different answers to score as equal. (Ignoring the font.) int epsilon_rank = 0; int answer_epsilon_rank = -1; int num_top_answers = 0; double prev_rating = results[0].rating; bool joined = false; bool broken = false; int res_index = 0; while (res_index < num_results) { if (results[res_index].rating < prev_rating - rating_epsilon_) { ++epsilon_rank; prev_rating = results[res_index].rating; } if (results[res_index].unichar_id == unichar_id && answer_epsilon_rank < 0) { answer_epsilon_rank = epsilon_rank; answer_actual_rank = res_index; } if (results[res_index].unichar_id == UNICHAR_JOINED && unicharset_.has_special_codes()) joined = true; else if (results[res_index].unichar_id == UNICHAR_BROKEN && unicharset_.has_special_codes()) broken = true; else if (epsilon_rank == 0) ++num_top_answers; ++res_index; } if (answer_actual_rank != 0) { // Correct result is not absolute top. ++font_counts_[font_id].n[CT_UNICHAR_TOPTOP_ERR]; if (boosting_mode == CT_UNICHAR_TOPTOP_ERR) sample->set_is_error(true); } if (answer_epsilon_rank == 0) { ++font_counts_[font_id].n[CT_UNICHAR_TOP_OK]; // Unichar OK, but count if multiple unichars. if (num_top_answers > 1) { ++font_counts_[font_id].n[CT_OK_MULTI_UNICHAR]; ++multi_unichar_counts_[unichar_id]; } // Check to see if any font in the top choice has attributes that match. // TODO(rays) It is easy to add counters for individual font attributes // here if we want them. if (font_table.SetContainsFontProperties( font_id, results[answer_actual_rank].fonts)) { // Font attributes were matched. // Check for multiple properties. if (font_table.SetContainsMultipleFontProperties( results[answer_actual_rank].fonts)) ++font_counts_[font_id].n[CT_OK_MULTI_FONT]; } else { // Font attributes weren't matched. ++font_counts_[font_id].n[CT_FONT_ATTR_ERR]; } } else { // This is a top unichar error. ++font_counts_[font_id].n[CT_UNICHAR_TOP1_ERR]; if (boosting_mode == CT_UNICHAR_TOP1_ERR) sample->set_is_error(true); // Count maps from unichar id to wrong unichar id. ++unichar_counts_(unichar_id, results[0].unichar_id); if (answer_epsilon_rank < 0 || answer_epsilon_rank >= 2) { // It is also a 2nd choice unichar error. ++font_counts_[font_id].n[CT_UNICHAR_TOP2_ERR]; if (boosting_mode == CT_UNICHAR_TOP2_ERR) sample->set_is_error(true); } if (answer_epsilon_rank < 0) { // It is also a top-n choice unichar error. ++font_counts_[font_id].n[CT_UNICHAR_TOPN_ERR]; if (boosting_mode == CT_UNICHAR_TOPN_ERR) sample->set_is_error(true); answer_epsilon_rank = epsilon_rank; } } // Compute mean number of return values and mean rank of correct answer. font_counts_[font_id].n[CT_NUM_RESULTS] += num_results; font_counts_[font_id].n[CT_RANK] += answer_epsilon_rank; if (joined) ++font_counts_[font_id].n[CT_OK_JOINED]; if (broken) ++font_counts_[font_id].n[CT_OK_BROKEN]; } // If it was an error for boosting then sum the weight. if (sample->is_error()) { scaled_error_ += sample->weight(); if (debug) { tprintf("%d results for char %s font %d :", num_results, unicharset_.id_to_unichar(unichar_id), font_id); for (int i = 0; i < num_results; ++i) { tprintf(" %.3f : %s\n", results[i].rating, unicharset_.id_to_unichar(results[i].unichar_id)); } return true; } int percent = 0; if (num_results > 0) percent = IntCastRounded(results[0].rating * 100); bad_score_hist_.add(percent, 1); } else { int percent = 0; if (answer_actual_rank >= 0) percent = IntCastRounded(results[answer_actual_rank].rating * 100); ok_score_hist_.add(percent, 1); } return false; } // Accumulates counts for junk. Counts only whether the junk was correctly // rejected or not. bool ErrorCounter::AccumulateJunk(bool debug, const GenericVector& results, TrainingSample* sample) { // For junk we accept no answer, or an explicit shape answer matching the // class id of the sample. int num_results = results.size(); int font_id = sample->font_id(); int unichar_id = sample->class_id(); int percent = 0; if (num_results > 0) percent = IntCastRounded(results[0].rating * 100); if (num_results > 0 && results[0].unichar_id != unichar_id) { // This is a junk error. ++font_counts_[font_id].n[CT_ACCEPTED_JUNK]; sample->set_is_error(true); // It counts as an error for boosting too so sum the weight. scaled_error_ += sample->weight(); bad_score_hist_.add(percent, 1); return debug; } else { // Correctly rejected. ++font_counts_[font_id].n[CT_REJECTED_JUNK]; sample->set_is_error(false); ok_score_hist_.add(percent, 1); } return false; } // Creates a report of the error rate. The report_level controls the detail // that is reported to stderr via tprintf: // 0 -> no output. // >=1 -> bottom-line error rate. // >=3 -> font-level error rate. // boosting_mode determines the return value. It selects which (un-weighted) // error rate to return. // The fontinfo_table from MasterTrainer provides the names of fonts. // The it determines the current subset of the training samples. // If not NULL, the top-choice unichar error rate is saved in unichar_error. // If not NULL, the report string is saved in fonts_report. // (Ignoring report_level). double ErrorCounter::ReportErrors(int report_level, CountTypes boosting_mode, const FontInfoTable& fontinfo_table, const SampleIterator& it, double* unichar_error, STRING* fonts_report) { // Compute totals over all the fonts and report individual font results // when required. Counts totals; int fontsize = font_counts_.size(); for (int f = 0; f < fontsize; ++f) { // Accumulate counts over fonts. totals += font_counts_[f]; STRING font_report; if (ReportString(false, font_counts_[f], &font_report)) { if (fonts_report != NULL) { *fonts_report += fontinfo_table.get(f).name; *fonts_report += ": "; *fonts_report += font_report; *fonts_report += "\n"; } if (report_level > 2) { // Report individual font error rates. tprintf("%s: %s\n", fontinfo_table.get(f).name, font_report.string()); } } } // Report the totals. STRING total_report; bool any_results = ReportString(true, totals, &total_report); if (fonts_report != NULL && fonts_report->length() == 0) { // Make sure we return something even if there were no samples. *fonts_report = "NoSamplesFound: "; *fonts_report += total_report; *fonts_report += "\n"; } if (report_level > 0) { // Report the totals. STRING total_report; if (any_results) { tprintf("TOTAL Scaled Err=%.4g%%, %s\n", scaled_error_ * 100.0, total_report.string()); } // Report the worst substitution error only for now. if (totals.n[CT_UNICHAR_TOP1_ERR] > 0) { int charsetsize = unicharset_.size(); int worst_uni_id = 0; int worst_result_id = 0; int worst_err = 0; for (int u = 0; u < charsetsize; ++u) { for (int v = 0; v < charsetsize; ++v) { if (unichar_counts_(u, v) > worst_err) { worst_err = unichar_counts_(u, v); worst_uni_id = u; worst_result_id = v; } } } if (worst_err > 0) { tprintf("Worst error = %d:%s -> %s with %d/%d=%.2f%% errors\n", worst_uni_id, unicharset_.id_to_unichar(worst_uni_id), unicharset_.id_to_unichar(worst_result_id), worst_err, totals.n[CT_UNICHAR_TOP1_ERR], 100.0 * worst_err / totals.n[CT_UNICHAR_TOP1_ERR]); } } tprintf("Multi-unichar shape use:\n"); for (int u = 0; u < multi_unichar_counts_.size(); ++u) { if (multi_unichar_counts_[u] > 0) { tprintf("%d multiple answers for unichar: %s\n", multi_unichar_counts_[u], unicharset_.id_to_unichar(u)); } } tprintf("OK Score histogram:\n"); ok_score_hist_.print(); tprintf("ERROR Score histogram:\n"); bad_score_hist_.print(); } double rates[CT_SIZE]; if (!ComputeRates(totals, rates)) return 0.0; // Set output values if asked for. if (unichar_error != NULL) *unichar_error = rates[CT_UNICHAR_TOP1_ERR]; return rates[boosting_mode]; } // Sets the report string to a combined human and machine-readable report // string of the error rates. // Returns false if there is no data, leaving report unchanged, unless // even_if_empty is true. bool ErrorCounter::ReportString(bool even_if_empty, const Counts& counts, STRING* report) { // Compute the error rates. double rates[CT_SIZE]; if (!ComputeRates(counts, rates) && !even_if_empty) return false; // Using %.4g%%, the length of the output string should exactly match the // length of the format string, but in case of overflow, allow for +eddd // on each number. const int kMaxExtraLength = 5; // Length of +eddd. // Keep this format string and the snprintf in sync with the CountTypes enum. const char* format_str = "Unichar=%.4g%%[1], %.4g%%[2], %.4g%%[n], %.4g%%[T] " "Mult=%.4g%%, Jn=%.4g%%, Brk=%.4g%%, Rej=%.4g%%, " "FontAttr=%.4g%%, Multi=%.4g%%, " "Answers=%.3g, Rank=%.3g, " "OKjunk=%.4g%%, Badjunk=%.4g%%"; int max_str_len = strlen(format_str) + kMaxExtraLength * (CT_SIZE - 1) + 1; char* formatted_str = new char[max_str_len]; snprintf(formatted_str, max_str_len, format_str, rates[CT_UNICHAR_TOP1_ERR] * 100.0, rates[CT_UNICHAR_TOP2_ERR] * 100.0, rates[CT_UNICHAR_TOPN_ERR] * 100.0, rates[CT_UNICHAR_TOPTOP_ERR] * 100.0, rates[CT_OK_MULTI_UNICHAR] * 100.0, rates[CT_OK_JOINED] * 100.0, rates[CT_OK_BROKEN] * 100.0, rates[CT_REJECT] * 100.0, rates[CT_FONT_ATTR_ERR] * 100.0, rates[CT_OK_MULTI_FONT] * 100.0, rates[CT_NUM_RESULTS], rates[CT_RANK], 100.0 * rates[CT_REJECTED_JUNK], 100.0 * rates[CT_ACCEPTED_JUNK]); *report = formatted_str; delete [] formatted_str; // Now append each field of counts with a tab in front so the result can // be loaded into a spreadsheet. for (int ct = 0; ct < CT_SIZE; ++ct) report->add_str_int("\t", counts.n[ct]); return true; } // Computes the error rates and returns in rates which is an array of size // CT_SIZE. Returns false if there is no data, leaving rates unchanged. bool ErrorCounter::ComputeRates(const Counts& counts, double rates[CT_SIZE]) { int ok_samples = counts.n[CT_UNICHAR_TOP_OK] + counts.n[CT_UNICHAR_TOP1_ERR] + counts.n[CT_REJECT]; int junk_samples = counts.n[CT_REJECTED_JUNK] + counts.n[CT_ACCEPTED_JUNK]; // Compute rates for normal chars. double denominator = static_cast(MAX(ok_samples, 1)); for (int ct = 0; ct <= CT_RANK; ++ct) rates[ct] = counts.n[ct] / denominator; // Compute rates for junk. denominator = static_cast(MAX(junk_samples, 1)); for (int ct = CT_REJECTED_JUNK; ct <= CT_ACCEPTED_JUNK; ++ct) rates[ct] = counts.n[ct] / denominator; return ok_samples != 0 || junk_samples != 0; } ErrorCounter::Counts::Counts() { memset(n, 0, sizeof(n[0]) * CT_SIZE); } // Adds other into this for computing totals. void ErrorCounter::Counts::operator+=(const Counts& other) { for (int ct = 0; ct < CT_SIZE; ++ct) n[ct] += other.n[ct]; } } // namespace tesseract. tesseract-3.04.01/classify/errorcounter.h000066400000000000000000000250601266071204500204010ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) // // 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 THIRD_PARTY_TESSERACT_CLASSIFY_ERRORCOUNTER_H_ #define THIRD_PARTY_TESSERACT_CLASSIFY_ERRORCOUNTER_H_ #include "genericvector.h" #include "matrix.h" #include "statistc.h" struct Pix; template class UnicityTable; namespace tesseract { struct FontInfo; class FontInfoTable; class SampleIterator; class ShapeClassifier; class TrainingSample; struct UnicharRating; // Enumeration of the different types of error count. // Error counts work as follows: // // Ground truth is a valid unichar-id / font-id pair: // Number of classifier answers? // 0 >0 // CT_REJECT unichar-id matches top shape? // __________ yes! no // CT_UNICHAR_TOP_OK CT_UNICHAR_TOP1_ERR // Top shape-id has multiple unichars? 2nd shape unichar id matches? // yes! no yes! no // CT_OK_MULTI_UNICHAR | _____ CT_UNICHAR_TOP2_ERR // Font attributes match? Any unichar-id matches? // yes! no yes! no // CT_FONT_ATTR_OK CT_FONT_ATTR_ERR ______ CT_UNICHAR_TOPN_ERR // | __________________ _________________ // Top shape-id has multiple font attrs? // yes! no // CT_OK_MULTI_FONT // _____________________________ // // Note that multiple counts may be activated for a single sample! // // Ground truth is for a fragment/n-gram that is NOT in the unicharset. // This is called junk and is expected to be rejected: // Number of classifier answers? // 0 >0 // CT_REJECTED_JUNK CT_ACCEPTED_JUNK // // Also, CT_NUM_RESULTS stores the mean number of results, and CT_RANK stores // the mean rank of the correct result, counting from 0, and with an error // receiving the number of answers as the correct rank. // // Keep in sync with the ReportString function. enum CountTypes { CT_UNICHAR_TOP_OK, // Top shape contains correct unichar id. // The rank of the results in TOP1, TOP2, TOPN is determined by a gap of // kRatingEpsilon from the first result in each group. The real top choice // is measured using TOPTOP. CT_UNICHAR_TOP1_ERR, // Top shape does not contain correct unichar id. CT_UNICHAR_TOP2_ERR, // Top 2 shapes don't contain correct unichar id. CT_UNICHAR_TOPN_ERR, // No output shape contains correct unichar id. CT_UNICHAR_TOPTOP_ERR, // Very top choice not correct. CT_OK_MULTI_UNICHAR, // Top shape id has correct unichar id, and others. CT_OK_JOINED, // Top shape id is correct but marked joined. CT_OK_BROKEN, // Top shape id is correct but marked broken. CT_REJECT, // Classifier hates this. CT_FONT_ATTR_ERR, // Top unichar OK, but font attributes incorrect. CT_OK_MULTI_FONT, // CT_FONT_ATTR_OK but there are multiple font attrs. CT_NUM_RESULTS, // Number of answers produced. CT_RANK, // Rank of correct answer. CT_REJECTED_JUNK, // Junk that was correctly rejected. CT_ACCEPTED_JUNK, // Junk that was incorrectly classified otherwise. CT_SIZE // Number of types for array sizing. }; // Class to encapsulate all the functionality and sub-structures required // to count errors for an isolated character classifier (ShapeClassifier). class ErrorCounter { public: // Computes and returns the unweighted boosting_mode error rate of the given // classifier. Can be used for testing, or inside an iterative training // system, including one that uses boosting. // report_levels: // 0 = no output. // 1 = bottom-line error rate. // 2 = bottom-line error rate + time. // 3 = font-level error rate + time. // 4 = list of all errors + short classifier debug output on 16 errors. // 5 = list of all errors + short classifier debug output on 25 errors. // * The boosting_mode determines which error type is used for computing the // scaled_error output, and setting the is_error flag in the samples. // * The fontinfo_table is used to get string font names for the debug // output, and also to count font attributes errors. // * The page_images vector may contain a Pix* (which may be NULL) for each // page index assigned to the samples. // * The it provides encapsulated iteration over some sample set. // * The outputs unichar_error, scaled_error and totals_report are all // optional. // * If not NULL, unichar error gets the top1 unichar error rate. // * Scaled_error gets the error chosen by boosting_mode weighted by the // weights on the samples. // * Fonts_report gets a string summarizing the error rates for each font in // both human-readable form and as a tab-separated list of error counts. // The human-readable form is all before the first tab. // * The return value is the un-weighted version of the scaled_error. static double ComputeErrorRate(ShapeClassifier* classifier, int report_level, CountTypes boosting_mode, const FontInfoTable& fontinfo_table, const GenericVector& page_images, SampleIterator* it, double* unichar_error, double* scaled_error, STRING* fonts_report); // Tests a pair of classifiers, debugging errors of the new against the old. // See errorcounter.h for description of arguments. // Iterates over the samples, calling the classifiers in normal/silent mode. // If the new_classifier makes a boosting_mode error that the old_classifier // does not, and the appropriate, it will then call the new_classifier again // with a debug flag and a keep_this argument to find out what is going on. static void DebugNewErrors(ShapeClassifier* new_classifier, ShapeClassifier* old_classifier, CountTypes boosting_mode, const FontInfoTable& fontinfo_table, const GenericVector& page_images, SampleIterator* it); private: // Simple struct to hold an array of counts. struct Counts { Counts(); // Adds other into this for computing totals. void operator+=(const Counts& other); int n[CT_SIZE]; }; // Constructor is private. Only anticipated use of ErrorCounter is via // the static ComputeErrorRate. ErrorCounter(const UNICHARSET& unicharset, int fontsize); ~ErrorCounter(); // Accumulates the errors from the classifier results on a single sample. // Returns true if debug is true and a CT_UNICHAR_TOPN_ERR error occurred. // boosting_mode selects the type of error to be used for boosting and the // is_error_ member of sample is set according to whether the required type // of error occurred. The font_table provides access to font properties // for error counting and shape_table is used to understand the relationship // between unichar_ids and shape_ids in the results bool AccumulateErrors(bool debug, CountTypes boosting_mode, const FontInfoTable& font_table, const GenericVector& results, TrainingSample* sample); // Accumulates counts for junk. Counts only whether the junk was correctly // rejected or not. bool AccumulateJunk(bool debug, const GenericVector& results, TrainingSample* sample); // Creates a report of the error rate. The report_level controls the detail // that is reported to stderr via tprintf: // 0 -> no output. // >=1 -> bottom-line error rate. // >=3 -> font-level error rate. // boosting_mode determines the return value. It selects which (un-weighted) // error rate to return. // The fontinfo_table from MasterTrainer provides the names of fonts. // The it determines the current subset of the training samples. // If not NULL, the top-choice unichar error rate is saved in unichar_error. // If not NULL, the report string is saved in fonts_report. // (Ignoring report_level). double ReportErrors(int report_level, CountTypes boosting_mode, const FontInfoTable& fontinfo_table, const SampleIterator& it, double* unichar_error, STRING* fonts_report); // Sets the report string to a combined human and machine-readable report // string of the error rates. // Returns false if there is no data, leaving report unchanged, unless // even_if_empty is true. static bool ReportString(bool even_if_empty, const Counts& counts, STRING* report); // Computes the error rates and returns in rates which is an array of size // CT_SIZE. Returns false if there is no data, leaving rates unchanged. static bool ComputeRates(const Counts& counts, double rates[CT_SIZE]); // Total scaled error used by boosting algorithms. double scaled_error_; // Difference in result rating to be thought of as an "equal" choice. double rating_epsilon_; // Vector indexed by font_id from the samples of error accumulators. GenericVector font_counts_; // Counts of the results that map each unichar_id (from samples) to an // incorrect shape_id. GENERIC_2D_ARRAY unichar_counts_; // Count of the number of times each shape_id occurs, is correct, and multi- // unichar. GenericVector multi_unichar_counts_; // Histogram of scores (as percent) for correct answers. STATS ok_score_hist_; // Histogram of scores (as percent) for incorrect answers. STATS bad_score_hist_; // Unicharset for printing character ids in results. const UNICHARSET& unicharset_; }; } // namespace tesseract. #endif /* THIRD_PARTY_TESSERACT_CLASSIFY_ERRORCOUNTER_H_ */ tesseract-3.04.01/classify/featdefs.cpp000066400000000000000000000250531266071204500177660ustar00rootroot00000000000000/****************************************************************************** ** Filename: featdefs.c ** Purpose: Definitions of currently defined feature types. ** Author: Dan Johnson ** History: Mon May 21 10:26:21 1990, 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 -----------------------------------------------------------------------------*/ #ifdef _MSC_VER #include #endif #include "featdefs.h" #include "emalloc.h" #include "danerror.h" #include "scanutils.h" #include #include /** define errors triggered by this module */ #define ILLEGAL_NUM_SETS 3001 #define PICO_FEATURE_LENGTH 0.05 /*----------------------------------------------------------------------------- Global Data Definitions and Declarations -----------------------------------------------------------------------------*/ const char* kMicroFeatureType = "mf"; const char* kCNFeatureType = "cn"; const char* kIntFeatureType = "if"; const char* kGeoFeatureType = "tb"; // Define all of the parameters for the MicroFeature type. StartParamDesc(MicroFeatureParams) DefineParam(0, 0, -0.5, 0.5) DefineParam(0, 0, -0.25, 0.75) DefineParam(0, 1, 0.0, 1.0) DefineParam(1, 0, 0.0, 1.0) DefineParam (0, 1, -0.5, 0.5) DefineParam (0, 1, -0.5, 0.5) EndParamDesc // Now define the feature type itself (see features.h for parameters). DefineFeature(MicroFeatureDesc, 5, 1, kMicroFeatureType, MicroFeatureParams) // Define all of the parameters for the NormFeat type. StartParamDesc (CharNormParams) DefineParam(0, 0, -0.25, 0.75) DefineParam(0, 1, 0.0, 1.0) DefineParam(0, 0, 0.0, 1.0) DefineParam(0, 0, 0.0, 1.0) EndParamDesc // Now define the feature type itself (see features.h for parameters). DefineFeature(CharNormDesc, 4, 0, kCNFeatureType, CharNormParams) // Define all of the parameters for the IntFeature type StartParamDesc(IntFeatParams) DefineParam(0, 0, 0.0, 255.0) DefineParam(0, 0, 0.0, 255.0) DefineParam(1, 0, 0.0, 255.0) EndParamDesc // Now define the feature type itself (see features.h for parameters). DefineFeature(IntFeatDesc, 2, 1, kIntFeatureType, IntFeatParams) // Define all of the parameters for the GeoFeature type StartParamDesc(GeoFeatParams) DefineParam(0, 0, 0.0, 255.0) DefineParam(0, 0, 0.0, 255.0) DefineParam(0, 0, 0.0, 255.0) EndParamDesc // Now define the feature type itself (see features.h for parameters). DefineFeature(GeoFeatDesc, 3, 0, kGeoFeatureType, GeoFeatParams) // Other features used for training the adaptive classifier, but not used // during normal training, therefore not in the DescDefs array. // Define all of the parameters for the PicoFeature type // define knob that can be used to adjust pico-feature length. FLOAT32 PicoFeatureLength = PICO_FEATURE_LENGTH; StartParamDesc(PicoFeatParams) DefineParam(0, 0, -0.25, 0.75) DefineParam(1, 0, 0.0, 1.0) DefineParam(0, 0, -0.5, 0.5) EndParamDesc // Now define the feature type itself (see features.h for parameters). DefineFeature(PicoFeatDesc, 2, 1, "pf", PicoFeatParams) // Define all of the parameters for the OutlineFeature type. StartParamDesc(OutlineFeatParams) DefineParam(0, 0, -0.5, 0.5) DefineParam(0, 0, -0.25, 0.75) DefineParam(0, 0, 0.0, 1.0) DefineParam(1, 0, 0.0, 1.0) EndParamDesc // Now define the feature type itself (see features.h for parameters). DefineFeature(OutlineFeatDesc, 3, 1, "of", OutlineFeatParams) // MUST be kept in-sync with ExtractorDefs in fxdefs.cpp. static const FEATURE_DESC_STRUCT *DescDefs[NUM_FEATURE_TYPES] = { &MicroFeatureDesc, &CharNormDesc, &IntFeatDesc, &GeoFeatDesc }; /*----------------------------------------------------------------------------- Public Code -----------------------------------------------------------------------------*/ void InitFeatureDefs(FEATURE_DEFS_STRUCT *featuredefs) { featuredefs->NumFeatureTypes = NUM_FEATURE_TYPES; for (int i = 0; i < NUM_FEATURE_TYPES; ++i) { featuredefs->FeatureDesc[i] = DescDefs[i]; } } /*---------------------------------------------------------------------------*/ /** * Release the memory consumed by the specified character * description and all of the features in that description. * * @param CharDesc character description to be deallocated * * Globals: * - none * * @note Exceptions: none * @note History: Wed May 23 13:52:19 1990, DSJ, Created. */ void FreeCharDescription(CHAR_DESC CharDesc) { int i; if (CharDesc) { for (i = 0; i < CharDesc->NumFeatureSets; i++) FreeFeatureSet (CharDesc->FeatureSets[i]); Efree(CharDesc); } } /* FreeCharDescription */ /*---------------------------------------------------------------------------*/ /** * Allocate a new character description, initialize its * feature sets to be empty, and return it. * * Globals: * - none * * @return New character description structure. * @note Exceptions: none * @note History: Wed May 23 15:27:10 1990, DSJ, Created. */ CHAR_DESC NewCharDescription(const FEATURE_DEFS_STRUCT &FeatureDefs) { CHAR_DESC CharDesc; int i; CharDesc = (CHAR_DESC) Emalloc (sizeof (CHAR_DESC_STRUCT)); CharDesc->NumFeatureSets = FeatureDefs.NumFeatureTypes; for (i = 0; i < CharDesc->NumFeatureSets; i++) CharDesc->FeatureSets[i] = NULL; return (CharDesc); } /* NewCharDescription */ /*---------------------------------------------------------------------------*/ /** * Appends a textual representation of CharDesc to str. * The format used is to write out the number of feature * sets which will be written followed by a representation of * each feature set. * * Each set starts with the short name for that feature followed * by a description of the feature set. Feature sets which are * not present are not written. * * @param FeatureDefs definitions of feature types/extractors * @param str string to append CharDesc to * @param CharDesc character description to write to File * * @note Exceptions: none * @note History: Wed May 23 17:21:18 1990, DSJ, Created. */ void WriteCharDescription(const FEATURE_DEFS_STRUCT& FeatureDefs, CHAR_DESC CharDesc, STRING* str) { int Type; int NumSetsToWrite = 0; for (Type = 0; Type < CharDesc->NumFeatureSets; Type++) if (CharDesc->FeatureSets[Type]) NumSetsToWrite++; str->add_str_int(" ", NumSetsToWrite); *str += "\n"; for (Type = 0; Type < CharDesc->NumFeatureSets; Type++) { if (CharDesc->FeatureSets[Type]) { *str += FeatureDefs.FeatureDesc[Type]->ShortName; *str += " "; WriteFeatureSet(CharDesc->FeatureSets[Type], str); } } } /* WriteCharDescription */ // Return whether all of the fields of the given feature set // are well defined (not inf or nan). bool ValidCharDescription(const FEATURE_DEFS_STRUCT &FeatureDefs, CHAR_DESC CharDesc) { bool anything_written = false; bool well_formed = true; for (int Type = 0; Type < CharDesc->NumFeatureSets; Type++) { if (CharDesc->FeatureSets[Type]) { for (int i = 0; i < CharDesc->FeatureSets[Type]->NumFeatures; i++) { FEATURE feat = CharDesc->FeatureSets[Type]->Features[i]; for (int p = 0; p < feat->Type->NumParams; p++) { if (isnan(feat->Params[p]) || isinf(feat->Params[p])) well_formed = false; else anything_written = true; } } } else { return false; } } return anything_written && well_formed; } /* ValidCharDescription */ /*---------------------------------------------------------------------------*/ /** * Read a character description from File, and return * a data structure containing this information. The data * is formatted as follows: * @verbatim NumberOfSets ShortNameForSet1 Set1 ShortNameForSet2 Set2 ... @endverbatim * * Globals: * - none * * @param FeatureDefs definitions of feature types/extractors * @param File open text file to read character description from * @return Character description read from File. * @note Exceptions: * - ILLEGAL_NUM_SETS * @note History: Wed May 23 17:32:48 1990, DSJ, Created. */ CHAR_DESC ReadCharDescription(const FEATURE_DEFS_STRUCT &FeatureDefs, FILE *File) { int NumSetsToRead; char ShortName[FEAT_NAME_SIZE]; CHAR_DESC CharDesc; int Type; if (tfscanf(File, "%d", &NumSetsToRead) != 1 || NumSetsToRead < 0 || NumSetsToRead > FeatureDefs.NumFeatureTypes) DoError (ILLEGAL_NUM_SETS, "Illegal number of feature sets"); CharDesc = NewCharDescription(FeatureDefs); for (; NumSetsToRead > 0; NumSetsToRead--) { tfscanf(File, "%s", ShortName); Type = ShortNameToFeatureType(FeatureDefs, ShortName); CharDesc->FeatureSets[Type] = ReadFeatureSet (File, FeatureDefs.FeatureDesc[Type]); } return (CharDesc); } // ReadCharDescription /*---------------------------------------------------------------------------*/ /** * Search through all features currently defined and return * the feature type for the feature with the specified short * name. Trap an error if the specified name is not found. * * Globals: * - none * * @param FeatureDefs definitions of feature types/extractors * @param ShortName short name of a feature type * @return Feature type which corresponds to ShortName. * @note Exceptions: * - ILLEGAL_SHORT_NAME * @note History: Wed May 23 15:36:05 1990, DSJ, Created. */ int ShortNameToFeatureType(const FEATURE_DEFS_STRUCT &FeatureDefs, const char *ShortName) { int i; for (i = 0; i < FeatureDefs.NumFeatureTypes; i++) if (!strcmp ((FeatureDefs.FeatureDesc[i]->ShortName), ShortName)) return (i); DoError (ILLEGAL_SHORT_NAME, "Illegal short name for a feature"); return 0; } // ShortNameToFeatureType tesseract-3.04.01/classify/featdefs.h000066400000000000000000000070161266071204500174320ustar00rootroot00000000000000/****************************************************************************** ** Filename: featdefs.h ** Purpose: Definitions of currently defined feature types. ** Author: Dan Johnson ** History: Mon May 21 08:28:01 1990, 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 FEATDEFS_H #define FEATDEFS_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "ocrfeatures.h" /* Enumerate the different types of features currently defined. */ #define NUM_FEATURE_TYPES 4 extern const char* kMicroFeatureType; extern const char* kCNFeatureType; extern const char* kIntFeatureType; extern const char* kGeoFeatureType; /* define error traps which can be triggered by this module.*/ #define ILLEGAL_SHORT_NAME 2000 /* A character is described by multiple sets of extracted features. Each set contains a number of features of a particular type, for example, a set of bays, or a set of closures, or a set of microfeatures. Each feature consists of a number of parameters. All features within a feature set contain the same number of parameters.*/ struct CHAR_DESC_STRUCT { uinT32 NumFeatureSets; FEATURE_SET FeatureSets[NUM_FEATURE_TYPES]; }; typedef CHAR_DESC_STRUCT *CHAR_DESC; struct FEATURE_DEFS_STRUCT { inT32 NumFeatureTypes; const FEATURE_DESC_STRUCT* FeatureDesc[NUM_FEATURE_TYPES]; int FeatureEnabled[NUM_FEATURE_TYPES]; }; typedef FEATURE_DEFS_STRUCT *FEATURE_DEFS; /*---------------------------------------------------------------------- Generic functions for manipulating character descriptions ----------------------------------------------------------------------*/ void InitFeatureDefs(FEATURE_DEFS_STRUCT *featuredefs); void FreeCharDescription(CHAR_DESC CharDesc); CHAR_DESC NewCharDescription(const FEATURE_DEFS_STRUCT &FeatureDefs); bool ValidCharDescription(const FEATURE_DEFS_STRUCT &FeatureDefs, CHAR_DESC CharDesc); void WriteCharDescription(const FEATURE_DEFS_STRUCT& FeatureDefs, CHAR_DESC CharDesc, STRING* str); CHAR_DESC ReadCharDescription(const FEATURE_DEFS_STRUCT &FeatureDefs, FILE *File); int ShortNameToFeatureType(const FEATURE_DEFS_STRUCT &FeatureDefs, const char *ShortName); /**---------------------------------------------------------------------------- Global Data Definitions and Declarations ----------------------------------------------------------------------------**/ extern const FEATURE_DESC_STRUCT MicroFeatureDesc; extern const FEATURE_DESC_STRUCT PicoFeatDesc; extern const FEATURE_DESC_STRUCT CharNormDesc; extern const FEATURE_DESC_STRUCT OutlineFeatDesc; extern const FEATURE_DESC_STRUCT IntFeatDesc; extern const FEATURE_DESC_STRUCT GeoFeatDesc; #endif tesseract-3.04.01/classify/float2int.cpp000066400000000000000000000111511266071204500201010ustar00rootroot00000000000000/****************************************************************************** ** Filename: float2int.c ** Purpose: Routines for converting float features to int features ** Author: Dan Johnson ** History: Wed Mar 13 07:47:48 1991, 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 "float2int.h" #include "normmatch.h" #include "mfoutline.h" #include "classify.h" #include "helpers.h" #include "picofeat.h" #define MAX_INT_CHAR_NORM (INT_CHAR_NORM_RANGE - 1) /*----------------------------------------------------------------------------- Public Code -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ namespace tesseract { /** * For each class in the unicharset, clears the corresponding * entry in char_norm_array. char_norm_array is indexed by unichar_id. * * Globals: * - none * * @param char_norm_array array to be cleared * * @note Exceptions: none * @note History: Wed Feb 20 11:20:54 1991, DSJ, Created. */ void Classify::ClearCharNormArray(uinT8* char_norm_array) { memset(char_norm_array, 0, sizeof(*char_norm_array) * unicharset.size()); } /* ClearCharNormArray */ /*---------------------------------------------------------------------------*/ /** * For each class in unicharset, computes the match between * norm_feature and the normalization protos for that class. * Converts this number to the range from 0 - 255 and stores it * into char_norm_array. CharNormArray is indexed by unichar_id. * * Globals: * - PreTrainedTemplates current set of built-in templates * * @param norm_feature character normalization feature * @param[out] char_norm_array place to put results of size unicharset.size() * * @note Exceptions: none * @note History: Wed Feb 20 11:20:54 1991, DSJ, Created. */ void Classify::ComputeIntCharNormArray(const FEATURE_STRUCT& norm_feature, uinT8* char_norm_array) { for (int i = 0; i < unicharset.size(); i++) { if (i < PreTrainedTemplates->NumClasses) { int norm_adjust = static_cast(INT_CHAR_NORM_RANGE * ComputeNormMatch(i, norm_feature, FALSE)); char_norm_array[i] = ClipToRange(norm_adjust, 0, MAX_INT_CHAR_NORM); } else { // Classes with no templates (eg. ambigs & ligatures) default // to worst match. char_norm_array[i] = MAX_INT_CHAR_NORM; } } } /* ComputeIntCharNormArray */ /*---------------------------------------------------------------------------*/ /** * This routine converts each floating point pico-feature * in Features into integer format and saves it into * IntFeatures. * * Globals: * - none * * @param Features floating point pico-features to be converted * @param[out] IntFeatures array to put converted features into * * @note Exceptions: none * @note History: Wed Feb 20 10:58:45 1991, DSJ, Created. */ void Classify::ComputeIntFeatures(FEATURE_SET Features, INT_FEATURE_ARRAY IntFeatures) { int Fid; FEATURE Feature; FLOAT32 YShift; if (classify_norm_method == baseline) YShift = BASELINE_Y_SHIFT; else YShift = Y_SHIFT; for (Fid = 0; Fid < Features->NumFeatures; Fid++) { Feature = Features->Features[Fid]; IntFeatures[Fid].X = Bucket8For(Feature->Params[PicoFeatX], X_SHIFT, INT_FEAT_RANGE); IntFeatures[Fid].Y = Bucket8For(Feature->Params[PicoFeatY], YShift, INT_FEAT_RANGE); IntFeatures[Fid].Theta = CircBucketFor(Feature->Params[PicoFeatDir], ANGLE_SHIFT, INT_FEAT_RANGE); IntFeatures[Fid].CP_misses = 0; } } /* ComputeIntFeatures */ } // namespace tesseract tesseract-3.04.01/classify/float2int.h000066400000000000000000000024461266071204500175550ustar00rootroot00000000000000/****************************************************************************** ** Filename: float2int.h ** Purpose: Routines for converting float features to int features ** Author: Dan Johnson ** History: Wed Mar 13 08:06:41 1991, 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 FLOAT2INT_H #define FLOAT2INT_H /*----------------------------------------------------------------------------- Include Files and Type Defines -----------------------------------------------------------------------------*/ #include "intmatcher.h" #include "ocrfeatures.h" #define INT_FEAT_RANGE 256 #define BASELINE_Y_SHIFT (0.25) #endif tesseract-3.04.01/classify/fpoint.cpp000066400000000000000000000045431266071204500175050ustar00rootroot00000000000000/****************************************************************************** ** Filename: fpoint.c ** Purpose: Abstract data type for a 2D point (floating point coords) ** Author: Dan Johnson ** History: Thu Apr 12 10:44:15 1990, 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 "const.h" #include "fpoint.h" #include #include /*---------------------------------------------------------------------------- Public Code ----------------------------------------------------------------------------*/ FLOAT32 DistanceBetween(FPOINT A, FPOINT B) { double xd = XDelta(A, B); double yd = YDelta(A, B); return sqrt(static_cast(xd * xd + yd * yd)); } /** * Return the angle from Point1 to Point2 normalized to * lie in the range 0 to FullScale (where FullScale corresponds * to 2*pi or 360 degrees). * @param Point1 points to compute angle between * @param Point2 points to compute angle between * @param FullScale value to associate with 2*pi * @return none * @note Globals: none * @note Exceptions: none * @note History: Wed Mar 28 14:27:25 1990, DSJ, Created. */ FLOAT32 NormalizedAngleFrom(FPOINT *Point1, FPOINT *Point2, FLOAT32 FullScale) { FLOAT32 Angle; FLOAT32 NumRadsInCircle = 2.0 * PI; Angle = AngleFrom (*Point1, *Point2); if (Angle < 0.0) Angle += NumRadsInCircle; Angle *= FullScale / NumRadsInCircle; if (Angle < 0.0 || Angle >= FullScale) Angle = 0.0; return (Angle); } tesseract-3.04.01/classify/fpoint.h000066400000000000000000000043201266071204500171430ustar00rootroot00000000000000/****************************************************************************** ** Filename: fpoint.h ** Purpose: Abstract data type for 2D points (floating point coords) ** Author: Dan Johnson ** History: Thu Apr 12 10:50:01 1990, 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 FPOINT_H #define FPOINT_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "host.h" #include #include /* define data structure to hold 2D points or vectors using floating point */ typedef struct { FLOAT32 x, y; } FPOINT; typedef FPOINT FVECTOR; /**---------------------------------------------------------------------------- Macros ----------------------------------------------------------------------------**/ /* macros for computing miscellaneous functions of 2 points */ #define XDelta(A,B) ( (B).x - (A).x ) #define YDelta(A,B) ( (B).y - (A).y ) #define SlopeFrom(A,B) ( YDelta(A,B) / XDelta(A,B) ) #define AngleFrom(A,B) ( atan2((double) YDelta(A,B), \ (double) XDelta(A,B) ) ) #define XIntersectionOf(A,B,X) ( SlopeFrom(A,B) * ((X) - A.x) + A.y) /*------------------------------------------------------------------------- Public Function Prototypes ---------------------------------------------------------------------------*/ FLOAT32 DistanceBetween(FPOINT A, FPOINT B); FLOAT32 NormalizedAngleFrom(FPOINT *Point1, FPOINT *Point2, FLOAT32 FullScale); #endif tesseract-3.04.01/classify/intfeaturedist.cpp000066400000000000000000000122271266071204500212360ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: intfeaturedist.cpp // Description: Fast set-difference-based feature distance calculator. // Created: Thu Sep 01 13:07:30 PDT 2011 // // 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 "intfeaturedist.h" #include "intfeaturemap.h" namespace tesseract { IntFeatureDist::IntFeatureDist() : size_(0), total_feature_weight_(0.0), feature_map_(NULL), features_(NULL), features_delta_one_(NULL), features_delta_two_(NULL) { } IntFeatureDist::~IntFeatureDist() { Clear(); } // Initialize the table to the given size of feature space. void IntFeatureDist::Init(const IntFeatureMap* feature_map) { size_ = feature_map->sparse_size(); Clear(); feature_map_ = feature_map; features_ = new bool[size_]; features_delta_one_ = new bool[size_]; features_delta_two_ = new bool[size_]; memset(features_, false, size_ * sizeof(features_[0])); memset(features_delta_one_, false, size_ * sizeof(features_delta_one_[0])); memset(features_delta_two_, false, size_ * sizeof(features_delta_two_[0])); total_feature_weight_ = 0.0; } // Setup the map for the given indexed_features that have been indexed by // feature_map. void IntFeatureDist::Set(const GenericVector& indexed_features, int canonical_count, bool value) { total_feature_weight_ = canonical_count; for (int i = 0; i < indexed_features.size(); ++i) { int f = indexed_features[i]; features_[f] = value; for (int dir = -kNumOffsetMaps; dir <= kNumOffsetMaps; ++dir) { if (dir == 0) continue; int mapped_f = feature_map_->OffsetFeature(f, dir); if (mapped_f >= 0) { features_delta_one_[mapped_f] = value; for (int dir2 = -kNumOffsetMaps; dir2 <= kNumOffsetMaps; ++dir2) { if (dir2 == 0) continue; int mapped_f2 = feature_map_->OffsetFeature(mapped_f, dir2); if (mapped_f2 >= 0) features_delta_two_[mapped_f2] = value; } } } } } // Compute the distance between the given feature vector and the last // Set feature vector. double IntFeatureDist::FeatureDistance( const GenericVector& features) const { int num_test_features = features.size(); double denominator = total_feature_weight_ + num_test_features; double misses = denominator; for (int i = 0; i < num_test_features; ++i) { int index = features[i]; double weight = 1.0; if (features_[index]) { // A perfect match. misses -= 2.0 * weight; } else if (features_delta_one_[index]) { misses -= 1.5 * weight; } else if (features_delta_two_[index]) { // A near miss. misses -= 1.0 * weight; } } return misses / denominator; } // Compute the distance between the given feature vector and the last // Set feature vector. double IntFeatureDist::DebugFeatureDistance( const GenericVector& features) const { int num_test_features = features.size(); double denominator = total_feature_weight_ + num_test_features; double misses = denominator; for (int i = 0; i < num_test_features; ++i) { int index = features[i]; double weight = 1.0; INT_FEATURE_STRUCT f = feature_map_->InverseMapFeature(features[i]); tprintf("Testing feature weight %g:", weight); f.print(); if (features_[index]) { // A perfect match. misses -= 2.0 * weight; tprintf("Perfect hit\n"); } else if (features_delta_one_[index]) { misses -= 1.5 * weight; tprintf("-1 hit\n"); } else if (features_delta_two_[index]) { // A near miss. misses -= 1.0 * weight; tprintf("-2 hit\n"); } else { tprintf("Total miss\n"); } } tprintf("Features present:"); for (int i = 0; i < size_; ++i) { if (features_[i]) { INT_FEATURE_STRUCT f = feature_map_->InverseMapFeature(i); f.print(); } } tprintf("\nMinus one features:"); for (int i = 0; i < size_; ++i) { if (features_delta_one_[i]) { INT_FEATURE_STRUCT f = feature_map_->InverseMapFeature(i); f.print(); } } tprintf("\nMinus two features:"); for (int i = 0; i < size_; ++i) { if (features_delta_two_[i]) { INT_FEATURE_STRUCT f = feature_map_->InverseMapFeature(i); f.print(); } } tprintf("\n"); return misses / denominator; } // Clear all data. void IntFeatureDist::Clear() { delete [] features_; features_ = NULL; delete [] features_delta_one_; features_delta_one_ = NULL; delete [] features_delta_two_; features_delta_two_ = NULL; } } // namespace tesseract tesseract-3.04.01/classify/intfeaturedist.h000066400000000000000000000062501266071204500207020ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: intfeaturedist.h // Description: Fast set-difference-based feature distance calculator. // Created: Thu Sep 01 12:14:30 PDT 2011 // // 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_CLASSIFY_INTFEATUREDIST_H_ #define TESSERACT_CLASSIFY_INTFEATUREDIST_H_ #include "genericvector.h" namespace tesseract { class IntFeatureMap; // Feature distance calculator designed to provide a fast distance calculation // based on set difference between a given feature set and many other feature // sets in turn. // Representation of a feature set as an array of bools that are sparsely // true, and companion arrays that allow fast feature set distance // calculations with allowance of offsets in position. // Init is expensive, so for greatest efficiency, to re-initialize for a new // feature set, use Set(..., false) on the SAME feature set as was used to // setup with Set(..., true), to return to its initialized state before // reuse with Set(..., true) on a new feature set. class IntFeatureDist { public: IntFeatureDist(); ~IntFeatureDist(); // Initialize the bool array to the given size of feature space. // The feature_map is just borrowed, and must exist for the entire // lifetime of the IntFeatureDist. void Init(const IntFeatureMap* feature_map); // Setup the map for the given indexed_features that have been indexed by // feature_map. After use, use Set(..., false) to reset to the initial state // as this is faster than calling Init for sparse spaces. void Set(const GenericVector& indexed_features, int canonical_count, bool value); // Compute the distance between the given feature vector and the last // Set feature vector. double FeatureDistance(const GenericVector& features) const; double DebugFeatureDistance(const GenericVector& features) const; private: // Clear all data. void Clear(); // Size of the indexed feature space. int size_; // Total weight of features currently stored in the maps. double total_feature_weight_; // Pointer to IntFeatureMap given at Init to find offset features. const IntFeatureMap* feature_map_; // Array of bools indicating presence of a feature. bool* features_; // Array indicating the presence of a feature offset by one unit. bool* features_delta_one_; // Array indicating the presence of a feature offset by two units. bool* features_delta_two_; }; } // namespace tesseract #endif // TESSERACT_CLASSIFY_INTFEATUREDIST_H_ tesseract-3.04.01/classify/intfeaturemap.cpp000066400000000000000000000222531266071204500210500ustar00rootroot00000000000000// Copyright 2010 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: intfeaturemap.cpp // Description: Encapsulation of IntFeatureSpace with IndexMapBiDi // to provide a subspace mapping and fast feature lookup. // Created: Tue Oct 26 08:58:30 PDT 2010 // // 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 "intfeaturemap.h" #include "intfeaturespace.h" #include "intfx.h" // These includes do not exist yet, but will be coming soon. //#include "sampleiterator.h" //#include "trainingsample.h" //#include "trainingsampleset.h" namespace tesseract { const int kMaxOffsetDist = 32; const double kMinPCLengthIncrease = 1.0 / 1024; IntFeatureMap::IntFeatureMap() : mapping_changed_(true), compact_size_(0) { for (int dir = 0; dir < kNumOffsetMaps; ++dir) { offset_plus_[dir] = NULL; offset_minus_[dir] = NULL; } } IntFeatureMap::~IntFeatureMap() { Clear(); } // Pseudo-accessors. int IntFeatureMap::IndexFeature(const INT_FEATURE_STRUCT& f) const { return feature_space_.Index(f); } int IntFeatureMap::MapFeature(const INT_FEATURE_STRUCT& f) const { return feature_map_.SparseToCompact(feature_space_.Index(f)); } int IntFeatureMap::MapIndexFeature(int index_feature) const { return feature_map_.SparseToCompact(index_feature); } INT_FEATURE_STRUCT IntFeatureMap::InverseIndexFeature(int index_feature) const { return feature_space_.PositionFromIndex(index_feature); } INT_FEATURE_STRUCT IntFeatureMap::InverseMapFeature(int map_feature) const { int index = feature_map_.CompactToSparse(map_feature); return feature_space_.PositionFromIndex(index); } void IntFeatureMap::DeleteMapFeature(int map_feature) { feature_map_.Merge(-1, map_feature); mapping_changed_ = true; } bool IntFeatureMap::IsMapFeatureDeleted(int map_feature) const { return feature_map_.IsCompactDeleted(map_feature); } // Copies the given feature_space and uses it as the index feature map // from INT_FEATURE_STRUCT. void IntFeatureMap::Init(const IntFeatureSpace& feature_space) { feature_space_ = feature_space; mapping_changed_ = false; int sparse_size = feature_space_.Size(); feature_map_.Init(sparse_size, true); feature_map_.Setup(); compact_size_ = feature_map_.CompactSize(); // Initialize look-up tables if needed. FCOORD dir = FeatureDirection(0); if (dir.x() == 0.0f && dir.y() == 0.0f) InitIntegerFX(); // Compute look-up tables to generate offset features. for (int dir = 0; dir < kNumOffsetMaps; ++dir) { delete [] offset_plus_[dir]; delete [] offset_minus_[dir]; offset_plus_[dir] = new int[sparse_size]; offset_minus_[dir] = new int[sparse_size]; } for (int dir = 1; dir <= kNumOffsetMaps; ++dir) { for (int i = 0; i < sparse_size; ++i) { int offset_index = ComputeOffsetFeature(i, dir); offset_plus_[dir - 1][i] = offset_index; offset_index = ComputeOffsetFeature(i, -dir); offset_minus_[dir - 1][i] = offset_index; } } } // Helper to return an offset index feature. In this context an offset // feature with a dir of +/-1 is a feature of a similar direction, // but shifted perpendicular to the direction of the feature. An offset // feature with a dir of +/-2 is feature at the same position, but rotated // by +/- one [compact] quantum. Returns the index of the generated offset // feature, or -1 if it doesn't exist. Dir should be in // [-kNumOffsetMaps, kNumOffsetMaps] to indicate the relative direction. // A dir of 0 is an identity transformation. // Both input and output are from the index(sparse) feature space, not // the mapped/compact feature space, but the offset feature is the minimum // distance moved from the input to guarantee that it maps to the next // available quantum in the mapped/compact space. int IntFeatureMap::OffsetFeature(int index_feature, int dir) const { if (dir > 0 && dir <= kNumOffsetMaps) return offset_plus_[dir - 1][index_feature]; else if (dir < 0 && -dir <= kNumOffsetMaps) return offset_minus_[-dir - 1][index_feature]; else if (dir == 0) return index_feature; else return -1; } //#define EXPERIMENT_ON #ifdef EXPERIMENT_ON // This code is commented out as SampleIterator and // TrainingSample are not reviewed/checked in yet, but these functions are a // useful indicator of how an IntFeatureMap is setup. // Computes the features used by the subset of samples defined by // the iterator and sets up the feature mapping. // Returns the size of the compacted feature space. int IntFeatureMap::FindNZFeatureMapping(SampleIterator* it) { feature_map_.Init(feature_space_.Size(), false); int total_samples = 0; for (it->Begin(); !it->AtEnd(); it->Next()) { const TrainingSample& sample = it->GetSample(); GenericVector features; feature_space_.IndexAndSortFeatures(sample.features(), sample.num_features(), &features); int num_features = features.size(); for (int f = 0; f < num_features; ++f) feature_map_.SetMap(features[f], true); ++total_samples; } feature_map_.Setup(); compact_size_ = feature_map_.CompactSize(); mapping_changed_ = true; FinalizeMapping(it); tprintf("%d non-zero features found in %d samples\n", compact_size_, total_samples); return compact_size_; } #endif // After deleting some features, finish setting up the mapping, and map // all the samples. Returns the size of the compacted feature space. int IntFeatureMap::FinalizeMapping(SampleIterator* it) { if (mapping_changed_) { feature_map_.CompleteMerges(); compact_size_ = feature_map_.CompactSize(); #ifdef EXPERIMENT_ON it->MapSampleFeatures(*this); #endif mapping_changed_ = false; } return compact_size_; } // Prints the map features from the set in human-readable form. void IntFeatureMap::DebugMapFeatures( const GenericVector& map_features) const { for (int i = 0; i < map_features.size(); ++i) { INT_FEATURE_STRUCT f = InverseMapFeature(map_features[i]); f.print(); } } void IntFeatureMap::Clear() { for (int dir = 0; dir < kNumOffsetMaps; ++dir) { delete [] offset_plus_[dir]; delete [] offset_minus_[dir]; offset_plus_[dir] = NULL; offset_minus_[dir] = NULL; } } // Helper to compute an offset index feature. In this context an offset // feature with a dir of +/-1 is a feature of a similar direction, // but shifted perpendicular to the direction of the feature. An offset // feature with a dir of +/-2 is feature at the same position, but rotated // by +/- one [compact] quantum. Returns the index of the generated offset // feature, or -1 if it doesn't exist. Dir should be in // [-kNumOffsetMaps, kNumOffsetMaps] to indicate the relative direction. // A dir of 0 is an identity transformation. // Both input and output are from the index(sparse) feature space, not // the mapped/compact feature space, but the offset feature is the minimum // distance moved from the input to guarantee that it maps to the next // available quantum in the mapped/compact space. int IntFeatureMap::ComputeOffsetFeature(int index_feature, int dir) const { INT_FEATURE_STRUCT f = InverseIndexFeature(index_feature); ASSERT_HOST(IndexFeature(f) == index_feature); if (dir == 0) { return index_feature; } else if (dir == 1 || dir == -1) { FCOORD feature_dir = FeatureDirection(f.Theta); FCOORD rotation90(0.0f, 1.0f); feature_dir.rotate(rotation90); // Find the nearest existing feature. for (int m = 1; m < kMaxOffsetDist; ++m) { double x_pos = f.X + feature_dir.x() * (m * dir); double y_pos = f.Y + feature_dir.y() * (m * dir); int x = IntCastRounded(x_pos); int y = IntCastRounded(y_pos); if (x >= 0 && x <= MAX_UINT8 && y >= 0 && y <= MAX_UINT8) { INT_FEATURE_STRUCT offset_f; offset_f.X = x; offset_f.Y = y; offset_f.Theta = f.Theta; int offset_index = IndexFeature(offset_f); if (offset_index != index_feature && offset_index >= 0) return offset_index; // Found one. } else { return -1; // Hit the edge of feature space. } } } else if (dir == 2 || dir == -2) { // Find the nearest existing index_feature. for (int m = 1; m < kMaxOffsetDist; ++m) { int theta = f.Theta + m * dir / 2; INT_FEATURE_STRUCT offset_f; offset_f.X = f.X; offset_f.Y = f.Y; offset_f.Theta = Modulo(theta, 256); int offset_index = IndexFeature(offset_f); if (offset_index != index_feature && offset_index >= 0) return offset_index; // Found one. } } return -1; // Nothing within the max distance. } } // namespace tesseract. tesseract-3.04.01/classify/intfeaturemap.h000066400000000000000000000161321266071204500205140ustar00rootroot00000000000000// Copyright 2010 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: intfeaturemap.h // Description: Encapsulation of IntFeatureSpace with IndexMapBiDi // to provide a subspace mapping and fast feature lookup. // Created: Tue Oct 26 08:58:30 PDT 2010 // // 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_CLASSIFY_INTFEATUREMAP_H__ #define TESSERACT_CLASSIFY_INTFEATUREMAP_H__ #include "intfeaturespace.h" #include "indexmapbidi.h" #include "intproto.h" namespace tesseract { class SampleIterator; // Number of positive and negative offset maps. static const int kNumOffsetMaps = 2; // Class to map a feature space defined by INT_FEATURE_STRUCT to a compact // down-sampled subspace of actually used features. // The IntFeatureMap copes with 2 stages of transformation: // The first step is down-sampling (re-quantization) and converting to a // single index value from the 3-D input: // INT_FEATURE_STRUCT <-> index feature (via IntFeatureSpace) and // the second is a feature-space compaction to map only the feature indices // that are actually used. This saves space in classifiers that are built // using the mapped feature space. // index (sparse) feature <-> map (compact) feature via IndexMapBiDi. // Although the transformations are reversible, the inverses are lossy and do // not return the exact input INT_FEATURE_STRUCT, due to the many->one nature // of both transformations. class IntFeatureMap { public: IntFeatureMap(); ~IntFeatureMap(); // Accessors. int sparse_size() const { return feature_space_.Size(); } int compact_size() const { return compact_size_; } const IntFeatureSpace& feature_space() const { return feature_space_; } const IndexMapBiDi& feature_map() const { return feature_map_; } // Pseudo-accessors. int IndexFeature(const INT_FEATURE_STRUCT& f) const; int MapFeature(const INT_FEATURE_STRUCT& f) const; int MapIndexFeature(int index_feature) const; INT_FEATURE_STRUCT InverseIndexFeature(int index_feature) const; INT_FEATURE_STRUCT InverseMapFeature(int map_feature) const; void DeleteMapFeature(int map_feature); bool IsMapFeatureDeleted(int map_feature) const; // Copies the given feature_space and uses it as the index feature map // from INT_FEATURE_STRUCT. void Init(const IntFeatureSpace& feature_space); // Helper to return an offset index feature. In this context an offset // feature with a dir of +/-1 is a feature of a similar direction, // but shifted perpendicular to the direction of the feature. An offset // feature with a dir of +/-2 is feature at the same position, but rotated // by +/- one [compact] quantum. Returns the index of the generated offset // feature, or -1 if it doesn't exist. Dir should be in // [-kNumOffsetMaps, kNumOffsetMaps] to indicate the relative direction. // A dir of 0 is an identity transformation. // Both input and output are from the index(sparse) feature space, not // the mapped/compact feature space, but the offset feature is the minimum // distance moved from the input to guarantee that it maps to the next // available quantum in the mapped/compact space. int OffsetFeature(int index_feature, int dir) const; // Computes the features used by the subset of samples defined by // the iterator and sets up the feature mapping. // Returns the size of the compacted feature space. int FindNZFeatureMapping(SampleIterator* it); // After deleting some features, finish setting up the mapping, and map // all the samples. Returns the size of the compacted feature space. int FinalizeMapping(SampleIterator* it); // Indexes the given array of features to a vector of sorted indices. void IndexAndSortFeatures(const INT_FEATURE_STRUCT* features, int num_features, GenericVector* sorted_features) const { feature_space_.IndexAndSortFeatures(features, num_features, sorted_features); } // Maps the given array of index/sparse features to an array of map/compact // features. // Assumes the input is sorted. The output indices are sorted and uniqued. // Returns the number of "missed" features, being features that // don't map to the compact feature space. int MapIndexedFeatures(const GenericVector& index_features, GenericVector* map_features) const { return feature_map_.MapFeatures(index_features, map_features); } // Prints the map features from the set in human-readable form. void DebugMapFeatures(const GenericVector& map_features) const; private: void Clear(); // Helper to compute an offset index feature. In this context an offset // feature with a dir of +/-1 is a feature of a similar direction, // but shifted perpendicular to the direction of the feature. An offset // feature with a dir of +/-2 is feature at the same position, but rotated // by +/- one [compact] quantum. Returns the index of the generated offset // feature, or -1 if it doesn't exist. Dir should be in // [-kNumOffsetMaps, kNumOffsetMaps] to indicate the relative direction. // A dir of 0 is an identity transformation. // Both input and output are from the index(sparse) feature space, not // the mapped/compact feature space, but the offset feature is the minimum // distance moved from the input to guarantee that it maps to the next // available quantum in the mapped/compact space. int ComputeOffsetFeature(int index_feature, int dir) const; // True if the mapping has changed since it was last finalized. bool mapping_changed_; // Size of the compacted feature space, after unused features are removed. int compact_size_; // Feature space quantization definition and indexing from INT_FEATURE_STRUCT. IntFeatureSpace feature_space_; // Mapping from indexed feature space to the compacted space with unused // features mapping to -1. IndexMapBiDi feature_map_; // Index tables to map a feature index to the corresponding feature after a // shift perpendicular to the feature direction, or a rotation in place. // An entry of -1 indicates that there is no corresponding feature. // Array of arrays of size feature_space_.Size() owned by this class. int* offset_plus_[kNumOffsetMaps]; int* offset_minus_[kNumOffsetMaps]; // Don't use default copy and assign! IntFeatureMap(const IntFeatureMap&); void operator=(const IntFeatureMap&); }; } // namespace tesseract. #endif // TESSERACT_CLASSIFY_INTFEATUREMAP_H__ tesseract-3.04.01/classify/intfeaturespace.cpp000066400000000000000000000120141266071204500213600ustar00rootroot00000000000000// Copyright 2010 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: intfeaturespace.cpp // Description: Indexed feature space based on INT_FEATURE_STRUCT. // Created: Wed Mar 24 11:21:27 PDT 2010 // // 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 "intfeaturespace.h" #include "intfx.h" namespace tesseract { IntFeatureSpace::IntFeatureSpace() : x_buckets_(0), y_buckets_(0), theta_buckets_(0) { } void IntFeatureSpace::Init(uinT8 xbuckets, uinT8 ybuckets, uinT8 thetabuckets) { x_buckets_ = xbuckets; y_buckets_ = ybuckets; theta_buckets_ = thetabuckets; } // Serializes the feature space definition to the given file. // Returns false on error. bool IntFeatureSpace::Serialize(FILE* fp) const { if (fwrite(&x_buckets_, sizeof(x_buckets_), 1, fp) != 1) return false; if (fwrite(&y_buckets_, sizeof(y_buckets_), 1, fp) != 1) return false; if (fwrite(&theta_buckets_, sizeof(theta_buckets_), 1, fp) != 1) return false; return true; } // DeSerializes the feature space definition from the given file. // If swap is true, the data is big/little-endian swapped. // Returns false on error. bool IntFeatureSpace::DeSerialize(bool swap, FILE* fp) { if (fread(&x_buckets_, sizeof(x_buckets_), 1, fp) != 1) return false; if (fread(&y_buckets_, sizeof(y_buckets_), 1, fp) != 1) return false; if (fread(&theta_buckets_, sizeof(theta_buckets_), 1, fp) != 1) return false; return true; } // Returns an INT_FEATURE_STRUCT corresponding to the given index. // This is the inverse of the Index member. INT_FEATURE_STRUCT IntFeatureSpace::PositionFromIndex(int index) const { return PositionFromBuckets(index / (y_buckets_ * theta_buckets_), index / theta_buckets_ % y_buckets_, index % theta_buckets_); } // Bulk calls to Index. Maps the given array of features to a vector of // inT32 indices in the same order as the input. void IntFeatureSpace::IndexFeatures(const INT_FEATURE_STRUCT* features, int num_features, GenericVector* mapped_features) const { mapped_features->truncate(0); for (int f = 0; f < num_features; ++f) mapped_features->push_back(Index(features[f])); } // Bulk calls to Index. Maps the given array of features to a vector of // sorted inT32 indices. void IntFeatureSpace::IndexAndSortFeatures( const INT_FEATURE_STRUCT* features, int num_features, GenericVector* sorted_features) const { sorted_features->truncate(0); for (int f = 0; f < num_features; ++f) sorted_features->push_back(Index(features[f])); sorted_features->sort(); } // Returns a feature space index for the given x,y position in a display // window, or -1 if the feature is a miss. int IntFeatureSpace::XYToFeatureIndex(int x, int y) const { // Round the x,y position to a feature. Search for a valid theta. INT_FEATURE_STRUCT feature(x, y, 0); int index = -1; for (int theta = 0; theta <= MAX_UINT8 && index < 0; ++theta) { feature.Theta = theta; index = Index(feature); } if (index < 0) { tprintf("(%d,%d) does not exist in feature space!\n", x, y); return -1; } feature = PositionFromIndex(index); tprintf("Click at (%d, %d) ->(%d, %d), ->(%d, %d)\n", x, y, feature.X, feature.Y, x - feature.X, y - feature.Y); // Get the relative position of x,y from the rounded feature. x -= feature.X; y -= feature.Y; if (x != 0 || y != 0) { double angle = atan2(static_cast(y), static_cast(x)) + PI; angle *= kIntFeatureExtent / (2.0 * PI); feature.Theta = static_cast(angle + 0.5); index = Index(feature); if (index < 0) { tprintf("Feature failed to map to a valid index:"); feature.print(); return -1; } feature = PositionFromIndex(index); } feature.print(); return index; } // Returns an INT_FEATURE_STRUCT corresponding to the given bucket coords. INT_FEATURE_STRUCT IntFeatureSpace::PositionFromBuckets(int x, int y, int theta) const { INT_FEATURE_STRUCT pos( (x * kIntFeatureExtent + kIntFeatureExtent / 2) / x_buckets_, (y * kIntFeatureExtent + kIntFeatureExtent / 2) / y_buckets_, DivRounded(theta * kIntFeatureExtent, theta_buckets_)); return pos; } } // namespace tesseract. tesseract-3.04.01/classify/intfeaturespace.h000066400000000000000000000104771266071204500210400ustar00rootroot00000000000000// Copyright 2010 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: intfeaturespace.h // Description: Indexed feature space based on INT_FEATURE_STRUCT. // Created: Wed Mar 24 10:55:30 PDT 2010 // // 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_CLASSIFY_INTFEATURESPACE_H__ #define TESSERACT_CLASSIFY_INTFEATURESPACE_H__ #include "genericvector.h" #include "intproto.h" // Extent of x,y,theta in the input feature space. [0,255]. const int kIntFeatureExtent = 256; // Extent of x,y,theta dimensions in the quantized feature space. const int kBoostXYBuckets = 16; const int kBoostDirBuckets = 16; namespace tesseract { class IndexMap; // Down-sampling quantization of the INT_FEATURE_STRUCT feature space and // conversion to a single scalar index value, used as a binary feature space. class IntFeatureSpace { public: IntFeatureSpace(); // Default copy constructors and assignment OK! // Setup the feature space with the given dimensions. void Init(uinT8 xbuckets, uinT8 ybuckets, uinT8 thetabuckets); // Serializes the feature space definition to the given file. // Returns false on error. bool Serialize(FILE* fp) const; // DeSerializes the feature space definition from the given file. // If swap is true, the data is big/little-endian swapped. // Returns false on error. bool DeSerialize(bool swap, FILE* fp); // Returns the total size of the feature space. int Size() const { return static_cast(x_buckets_) * y_buckets_ * theta_buckets_; } // Returns an INT_FEATURE_STRUCT corresponding to the given index. // This is the inverse of the Index member. INT_FEATURE_STRUCT PositionFromIndex(int index) const; // Returns a 1-dimensional index corresponding to the given feature value. // Range is [0, Size()-1]. Inverse of PositionFromIndex member. int Index(const INT_FEATURE_STRUCT& f) const { return (XBucket(f.X) * y_buckets_ + YBucket(f.Y)) * theta_buckets_ + ThetaBucket(f.Theta); } // Bulk calls to Index. Maps the given array of features to a vector of // inT32 indices in the same order as the input. void IndexFeatures(const INT_FEATURE_STRUCT* features, int num_features, GenericVector* mapped_features) const; // Bulk calls to Index. Maps the given array of features to a vector of // sorted inT32 indices. void IndexAndSortFeatures(const INT_FEATURE_STRUCT* features, int num_features, GenericVector* sorted_features) const; // Returns a feature space index for the given x,y position in a display // window, or -1 if the feature is a miss. int XYToFeatureIndex(int x, int y) const; protected: // Converters to generate indices for individual feature dimensions. int XBucket(int x) const { int bucket = x * x_buckets_ / kIntFeatureExtent; return ClipToRange(bucket, 0, static_cast(x_buckets_) - 1); } int YBucket(int y) const { int bucket = y * y_buckets_ / kIntFeatureExtent; return ClipToRange(bucket, 0, static_cast(y_buckets_) - 1); } // Use DivRounded for theta so that exactly vertical and horizontal are in // the middle of a bucket. The Modulo takes care of the wrap-around. int ThetaBucket(int theta) const { int bucket = DivRounded(theta * theta_buckets_, kIntFeatureExtent); return Modulo(bucket, theta_buckets_); } // Returns an INT_FEATURE_STRUCT corresponding to the given buckets. INT_FEATURE_STRUCT PositionFromBuckets(int x, int y, int theta) const; // Feature space definition - serialized. uinT8 x_buckets_; uinT8 y_buckets_; uinT8 theta_buckets_; }; } // namespace tesseract. #endif // TESSERACT_CLASSIFY_INTFEATURESPACE_H__ tesseract-3.04.01/classify/intfx.cpp000066400000000000000000000600261266071204500173340ustar00rootroot00000000000000/****************************************************************************** ** Filename: intfx.c ** Purpose: Integer character normalization & feature extraction ** Author: Robert Moss, rays@google.com (Ray Smith) ** History: Tue May 21 15:51:57 MDT 1991, RWM, Created. ** Tue Feb 28 10:42:00 PST 2012, vastly rewritten to allow greyscale fx and non-linear normalization. ** ** (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 "intfx.h" #include "allheaders.h" #include "ccutil.h" #include "classify.h" #include "const.h" #include "helpers.h" #include "intmatcher.h" #include "linlsq.h" #include "ndminx.h" #include "normalis.h" #include "statistc.h" #include "trainingsample.h" using tesseract::TrainingSample; /**---------------------------------------------------------------------------- Global Data Definitions and Declarations ----------------------------------------------------------------------------**/ // Look up table for cos and sin to turn the intfx feature angle to a vector. // Protected by atan_table_mutex. // The entries are in binary degrees where a full circle is 256 binary degrees. static float cos_table[INT_CHAR_NORM_RANGE]; static float sin_table[INT_CHAR_NORM_RANGE]; // Guards write access to AtanTable so we don't create it more than once. tesseract::CCUtilMutex atan_table_mutex; /**---------------------------------------------------------------------------- Public Code ----------------------------------------------------------------------------**/ /*---------------------------------------------------------------------------*/ void InitIntegerFX() { static bool atan_table_init = false; atan_table_mutex.Lock(); if (!atan_table_init) { for (int i = 0; i < INT_CHAR_NORM_RANGE; ++i) { cos_table[i] = cos(i * 2 * PI / INT_CHAR_NORM_RANGE + PI); sin_table[i] = sin(i * 2 * PI / INT_CHAR_NORM_RANGE + PI); } atan_table_init = true; } atan_table_mutex.Unlock(); } // Returns a vector representing the direction of a feature with the given // theta direction in an INT_FEATURE_STRUCT. FCOORD FeatureDirection(uinT8 theta) { return FCOORD(cos_table[theta], sin_table[theta]); } namespace tesseract { // Generates a TrainingSample from a TBLOB. Extracts features and sets // the bounding box, so classifiers that operate on the image can work. // TODO(rays) Make BlobToTrainingSample a member of Classify now that // the FlexFx and FeatureDescription code have been removed and LearnBlob // is now a member of Classify. TrainingSample* BlobToTrainingSample( const TBLOB& blob, bool nonlinear_norm, INT_FX_RESULT_STRUCT* fx_info, GenericVector* bl_features) { GenericVector cn_features; Classify::ExtractFeatures(blob, nonlinear_norm, bl_features, &cn_features, fx_info, NULL); // TODO(rays) Use blob->PreciseBoundingBox() instead. TBOX box = blob.bounding_box(); TrainingSample* sample = NULL; int num_features = fx_info->NumCN; if (num_features > 0) { sample = TrainingSample::CopyFromFeatures(*fx_info, box, &cn_features[0], num_features); } if (sample != NULL) { // Set the bounding box (in original image coordinates) in the sample. TPOINT topleft, botright; topleft.x = box.left(); topleft.y = box.top(); botright.x = box.right(); botright.y = box.bottom(); TPOINT original_topleft, original_botright; blob.denorm().DenormTransform(NULL, topleft, &original_topleft); blob.denorm().DenormTransform(NULL, botright, &original_botright); sample->set_bounding_box(TBOX(original_topleft.x, original_botright.y, original_botright.x, original_topleft.y)); } return sample; } // Computes the DENORMS for bl(baseline) and cn(character) normalization // during feature extraction. The input denorm describes the current state // of the blob, which is usually a baseline-normalized word. // The Transforms setup are as follows: // Baseline Normalized (bl) Output: // We center the grapheme by aligning the x-coordinate of its centroid with // x=128 and leaving the already-baseline-normalized y as-is. // // Character Normalized (cn) Output: // We align the grapheme's centroid at the origin and scale it // asymmetrically in x and y so that the 2nd moments are a standard value // (51.2) ie the result is vaguely square. // If classify_nonlinear_norm is true: // A non-linear normalization is setup that attempts to evenly distribute // edges across x and y. // // Some of the fields of fx_info are also setup: // Length: Total length of outline. // Rx: Rounded y second moment. (Reversed by convention.) // Ry: rounded x second moment. // Xmean: Rounded x center of mass of the blob. // Ymean: Rounded y center of mass of the blob. void Classify::SetupBLCNDenorms(const TBLOB& blob, bool nonlinear_norm, DENORM* bl_denorm, DENORM* cn_denorm, INT_FX_RESULT_STRUCT* fx_info) { // Compute 1st and 2nd moments of the original outline. FCOORD center, second_moments; int length = blob.ComputeMoments(¢er, &second_moments); if (fx_info != NULL) { fx_info->Length = length; fx_info->Rx = IntCastRounded(second_moments.y()); fx_info->Ry = IntCastRounded(second_moments.x()); fx_info->Xmean = IntCastRounded(center.x()); fx_info->Ymean = IntCastRounded(center.y()); } // Setup the denorm for Baseline normalization. bl_denorm->SetupNormalization(NULL, NULL, &blob.denorm(), center.x(), 128.0f, 1.0f, 1.0f, 128.0f, 128.0f); // Setup the denorm for character normalization. if (nonlinear_norm) { GenericVector > x_coords; GenericVector > y_coords; TBOX box; blob.GetPreciseBoundingBox(&box); box.pad(1, 1); blob.GetEdgeCoords(box, &x_coords, &y_coords); cn_denorm->SetupNonLinear(&blob.denorm(), box, MAX_UINT8, MAX_UINT8, 0.0f, 0.0f, x_coords, y_coords); } else { cn_denorm->SetupNormalization(NULL, NULL, &blob.denorm(), center.x(), center.y(), 51.2f / second_moments.x(), 51.2f / second_moments.y(), 128.0f, 128.0f); } } // Helper normalizes the direction, assuming that it is at the given // unnormed_pos, using the given denorm, starting at the root_denorm. uinT8 NormalizeDirection(uinT8 dir, const FCOORD& unnormed_pos, const DENORM& denorm, const DENORM* root_denorm) { // Convert direction to a vector. FCOORD unnormed_end; unnormed_end.from_direction(dir); unnormed_end += unnormed_pos; FCOORD normed_pos, normed_end; denorm.NormTransform(root_denorm, unnormed_pos, &normed_pos); denorm.NormTransform(root_denorm, unnormed_end, &normed_end); normed_end -= normed_pos; return normed_end.to_direction(); } // Helper returns the mean direction vector from the given stats. Use the // mean direction from dirs if there is information available, otherwise, use // the fit_vector from point_diffs. static FCOORD MeanDirectionVector(const LLSQ& point_diffs, const LLSQ& dirs, const FCOORD& start_pt, const FCOORD& end_pt) { FCOORD fit_vector; if (dirs.count() > 0) { // There were directions, so use them. To avoid wrap-around problems, we // have 2 accumulators in dirs: x for normal directions and y for // directions offset by 128. We will use the one with the least variance. FCOORD mean_pt = dirs.mean_point(); double mean_dir = 0.0; if (dirs.x_variance() <= dirs.y_variance()) { mean_dir = mean_pt.x(); } else { mean_dir = mean_pt.y() + 128; } fit_vector.from_direction(Modulo(IntCastRounded(mean_dir), 256)); } else { // There were no directions, so we rely on the vector_fit to the points. // Since the vector_fit is 180 degrees ambiguous, we align with the // supplied feature_dir by making the scalar product non-negative. FCOORD feature_dir(end_pt - start_pt); fit_vector = point_diffs.vector_fit(); if (fit_vector.x() == 0.0f && fit_vector.y() == 0.0f) { // There was only a single point. Use feature_dir directly. fit_vector = feature_dir; } else { // Sometimes the least mean squares fit is wrong, due to the small sample // of points and scaling. Use a 90 degree rotated vector if that matches // feature_dir better. FCOORD fit_vector2 = !fit_vector; // The fit_vector is 180 degrees ambiguous, so resolve the ambiguity by // insisting that the scalar product with the feature_dir should be +ve. if (fit_vector % feature_dir < 0.0) fit_vector = -fit_vector; if (fit_vector2 % feature_dir < 0.0) fit_vector2 = -fit_vector2; // Even though fit_vector2 has a higher mean squared error, it might be // a better fit, so use it if the dot product with feature_dir is bigger. if (fit_vector2 % feature_dir > fit_vector % feature_dir) fit_vector = fit_vector2; } } return fit_vector; } // Helper computes one or more features corresponding to the given points. // Emitted features are on the line defined by: // start_pt + lambda * (end_pt - start_pt) for scalar lambda. // Features are spaced at feature_length intervals. static int ComputeFeatures(const FCOORD& start_pt, const FCOORD& end_pt, double feature_length, GenericVector* features) { FCOORD feature_vector(end_pt - start_pt); if (feature_vector.x() == 0.0f && feature_vector.y() == 0.0f) return 0; // Compute theta for the feature based on its direction. uinT8 theta = feature_vector.to_direction(); // Compute the number of features and lambda_step. double target_length = feature_vector.length(); int num_features = IntCastRounded(target_length / feature_length); if (num_features == 0) return 0; // Divide the length evenly into num_features pieces. double lambda_step = 1.0 / num_features; double lambda = lambda_step / 2.0; for (int f = 0; f < num_features; ++f, lambda += lambda_step) { FCOORD feature_pt(start_pt); feature_pt += feature_vector * lambda; INT_FEATURE_STRUCT feature(feature_pt, theta); features->push_back(feature); } return num_features; } // Gathers outline points and their directions from start_index into dirs by // stepping along the outline and normalizing the coordinates until the // required feature_length has been collected or end_index is reached. // On input pos must point to the position corresponding to start_index and on // return pos is updated to the current raw position, and pos_normed is set to // the normed version of pos. // Since directions wrap-around, they need special treatment to get the mean. // Provided the cluster of directions doesn't straddle the wrap-around point, // the simple mean works. If they do, then, unless the directions are wildly // varying, the cluster rotated by 180 degrees will not straddle the wrap- // around point, so mean(dir + 180 degrees) - 180 degrees will work. Since // LLSQ conveniently stores the mean of 2 variables, we use it to store // dir and dir+128 (128 is 180 degrees) and then use the resulting mean // with the least variance. static int GatherPoints(const C_OUTLINE* outline, double feature_length, const DENORM& denorm, const DENORM* root_denorm, int start_index, int end_index, ICOORD* pos, FCOORD* pos_normed, LLSQ* points, LLSQ* dirs) { int step_length = outline->pathlength(); ICOORD step = outline->step(start_index % step_length); // Prev_normed is the start point of this collection and will be set on the // first iteration, and on later iterations used to determine the length // that has been collected. FCOORD prev_normed; points->clear(); dirs->clear(); int num_points = 0; int index; for (index = start_index; index <= end_index; ++index, *pos += step) { step = outline->step(index % step_length); int edge_weight = outline->edge_strength_at_index(index % step_length); if (edge_weight == 0) { // This point has conflicting gradient and step direction, so ignore it. continue; } // Get the sub-pixel precise location and normalize. FCOORD f_pos = outline->sub_pixel_pos_at_index(*pos, index % step_length); denorm.NormTransform(root_denorm, f_pos, pos_normed); if (num_points == 0) { // The start of this segment. prev_normed = *pos_normed; } else { FCOORD offset = *pos_normed - prev_normed; float length = offset.length(); if (length > feature_length) { // We have gone far enough from the start. We will use this point in // the next set so return what we have so far. return index; } } points->add(pos_normed->x(), pos_normed->y(), edge_weight); int direction = outline->direction_at_index(index % step_length); if (direction >= 0) { direction = NormalizeDirection(direction, f_pos, denorm, root_denorm); // Use both the direction and direction +128 so we are not trying to // take the mean of something straddling the wrap-around point. dirs->add(direction, Modulo(direction + 128, 256)); } ++num_points; } return index; } // Extracts Tesseract features and appends them to the features vector. // Startpt to lastpt, inclusive, MUST have the same src_outline member, // which may be NULL. The vector from lastpt to its next is included in // the feature extraction. Hidden edges should be excluded by the caller. // If force_poly is true, the features will be extracted from the polygonal // approximation even if more accurate data is available. static void ExtractFeaturesFromRun( const EDGEPT* startpt, const EDGEPT* lastpt, const DENORM& denorm, double feature_length, bool force_poly, GenericVector* features) { const EDGEPT* endpt = lastpt->next; const C_OUTLINE* outline = startpt->src_outline; if (outline != NULL && !force_poly) { // Detailed information is available. We have to normalize only from // the root_denorm to denorm. const DENORM* root_denorm = denorm.RootDenorm(); int total_features = 0; // Get the features from the outline. int step_length = outline->pathlength(); int start_index = startpt->start_step; // pos is the integer coordinates of the binary image steps. ICOORD pos = outline->position_at_index(start_index); // We use an end_index that allows us to use a positive increment, but that // may be beyond the bounds of the outline steps/ due to wrap-around, to // so we use % step_length everywhere, except for start_index. int end_index = lastpt->start_step + lastpt->step_count; if (end_index <= start_index) end_index += step_length; LLSQ prev_points; LLSQ prev_dirs; FCOORD prev_normed_pos = outline->sub_pixel_pos_at_index(pos, start_index); denorm.NormTransform(root_denorm, prev_normed_pos, &prev_normed_pos); LLSQ points; LLSQ dirs; FCOORD normed_pos; int index = GatherPoints(outline, feature_length, denorm, root_denorm, start_index, end_index, &pos, &normed_pos, &points, &dirs); while (index <= end_index) { // At each iteration we nominally have 3 accumulated sets of points and // dirs: prev_points/dirs, points/dirs, next_points/dirs and sum them // into sum_points/dirs, but we don't necessarily get any features out, // so if that is the case, we keep accumulating instead of rotating the // accumulators. LLSQ next_points; LLSQ next_dirs; FCOORD next_normed_pos; index = GatherPoints(outline, feature_length, denorm, root_denorm, index, end_index, &pos, &next_normed_pos, &next_points, &next_dirs); LLSQ sum_points(prev_points); // TODO(rays) find out why it is better to use just dirs and next_dirs // in sum_dirs, instead of using prev_dirs as well. LLSQ sum_dirs(dirs); sum_points.add(points); sum_points.add(next_points); sum_dirs.add(next_dirs); bool made_features = false; // If we have some points, we can try making some features. if (sum_points.count() > 0) { // We have gone far enough from the start. Make a feature and restart. FCOORD fit_pt = sum_points.mean_point(); FCOORD fit_vector = MeanDirectionVector(sum_points, sum_dirs, prev_normed_pos, normed_pos); // The segment to which we fit features is the line passing through // fit_pt in direction of fit_vector that starts nearest to // prev_normed_pos and ends nearest to normed_pos. FCOORD start_pos = prev_normed_pos.nearest_pt_on_line(fit_pt, fit_vector); FCOORD end_pos = normed_pos.nearest_pt_on_line(fit_pt, fit_vector); // Possible correction to match the adjacent polygon segment. if (total_features == 0 && startpt != endpt) { FCOORD poly_pos(startpt->pos.x, startpt->pos.y); denorm.LocalNormTransform(poly_pos, &start_pos); } if (index > end_index && startpt != endpt) { FCOORD poly_pos(endpt->pos.x, endpt->pos.y); denorm.LocalNormTransform(poly_pos, &end_pos); } int num_features = ComputeFeatures(start_pos, end_pos, feature_length, features); if (num_features > 0) { // We made some features so shuffle the accumulators. prev_points = points; prev_dirs = dirs; prev_normed_pos = normed_pos; points = next_points; dirs = next_dirs; made_features = true; total_features += num_features; } // The end of the next set becomes the end next time around. normed_pos = next_normed_pos; } if (!made_features) { // We didn't make any features, so keep the prev accumulators and // add the next ones into the current. points.add(next_points); dirs.add(next_dirs); } } } else { // There is no outline, so we are forced to use the polygonal approximation. const EDGEPT* pt = startpt; do { FCOORD start_pos(pt->pos.x, pt->pos.y); FCOORD end_pos(pt->next->pos.x, pt->next->pos.y); denorm.LocalNormTransform(start_pos, &start_pos); denorm.LocalNormTransform(end_pos, &end_pos); ComputeFeatures(start_pos, end_pos, feature_length, features); } while ((pt = pt->next) != endpt); } } // Extracts sets of 3-D features of length kStandardFeatureLength (=12.8), as // (x,y) position and angle as measured counterclockwise from the vector // <-1, 0>, from blob using two normalizations defined by bl_denorm and // cn_denorm. See SetpuBLCNDenorms for definitions. // If outline_cn_counts is not NULL, on return it contains the cumulative // number of cn features generated for each outline in the blob (in order). // Thus after the first outline, there were (*outline_cn_counts)[0] features, // after the second outline, there were (*outline_cn_counts)[1] features etc. void Classify::ExtractFeatures(const TBLOB& blob, bool nonlinear_norm, GenericVector* bl_features, GenericVector* cn_features, INT_FX_RESULT_STRUCT* results, GenericVector* outline_cn_counts) { DENORM bl_denorm, cn_denorm; tesseract::Classify::SetupBLCNDenorms(blob, nonlinear_norm, &bl_denorm, &cn_denorm, results); if (outline_cn_counts != NULL) outline_cn_counts->truncate(0); // Iterate the outlines. for (TESSLINE* ol = blob.outlines; ol != NULL; ol = ol->next) { // Iterate the polygon. EDGEPT* loop_pt = ol->FindBestStartPt(); EDGEPT* pt = loop_pt; if (pt == NULL) continue; do { if (pt->IsHidden()) continue; // Find a run of equal src_outline. EDGEPT* last_pt = pt; do { last_pt = last_pt->next; } while (last_pt != loop_pt && !last_pt->IsHidden() && last_pt->src_outline == pt->src_outline); last_pt = last_pt->prev; // Until the adaptive classifier can be weaned off polygon segments, // we have to force extraction from the polygon for the bl_features. ExtractFeaturesFromRun(pt, last_pt, bl_denorm, kStandardFeatureLength, true, bl_features); ExtractFeaturesFromRun(pt, last_pt, cn_denorm, kStandardFeatureLength, false, cn_features); pt = last_pt; } while ((pt = pt->next) != loop_pt); if (outline_cn_counts != NULL) outline_cn_counts->push_back(cn_features->size()); } results->NumBL = bl_features->size(); results->NumCN = cn_features->size(); results->YBottom = blob.bounding_box().bottom(); results->YTop = blob.bounding_box().top(); results->Width = blob.bounding_box().width(); } } // namespace tesseract /*--------------------------------------------------------------------------*/ // Extract a set of standard-sized features from Blobs and write them out in // two formats: baseline normalized and character normalized. // // We presume the Blobs are already scaled so that x-height=128 units // // Standard Features: // We take all outline segments longer than 7 units and chop them into // standard-sized segments of approximately 13 = (64 / 5) units. // When writing these features out, we output their center and angle as // measured counterclockwise from the vector <-1, 0> // // Baseline Normalized Output: // We center the grapheme by aligning the x-coordinate of its centroid with // x=0 and subtracting 128 from the y-coordinate. // // Character Normalized Output: // We align the grapheme's centroid at the origin and scale it asymmetrically // in x and y so that the result is vaguely square. // // Deprecated! Prefer tesseract::Classify::ExtractFeatures instead. bool ExtractIntFeat(const TBLOB& blob, bool nonlinear_norm, INT_FEATURE_ARRAY baseline_features, INT_FEATURE_ARRAY charnorm_features, INT_FX_RESULT_STRUCT* results) { GenericVector bl_features; GenericVector cn_features; tesseract::Classify::ExtractFeatures(blob, nonlinear_norm, &bl_features, &cn_features, results, NULL); if (bl_features.size() == 0 || cn_features.size() == 0 || bl_features.size() > MAX_NUM_INT_FEATURES || cn_features.size() > MAX_NUM_INT_FEATURES) { return false; // Feature extraction failed. } memcpy(baseline_features, &bl_features[0], bl_features.size() * sizeof(bl_features[0])); memcpy(charnorm_features, &cn_features[0], cn_features.size() * sizeof(cn_features[0])); return true; } tesseract-3.04.01/classify/intfx.h000066400000000000000000000060331266071204500167770ustar00rootroot00000000000000/****************************************************************************** ** Filename: intfx.h ** Purpose: Interface to high level integer feature extractor. ** Author: Robert Moss ** History: Tue May 21 15:51:57 MDT 1991, RWM, 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 INTFX_H #define INTFX_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "blobs.h" #include "intproto.h" #include "normalis.h" #include class DENORM; namespace tesseract { class TrainingSample; } struct INT_FX_RESULT_STRUCT { inT32 Length; // total length of all outlines inT16 Xmean, Ymean; // center of mass of all outlines inT16 Rx, Ry; // radius of gyration inT16 NumBL, NumCN; // number of features extracted inT16 Width; // Width of blob in BLN coords. uinT8 YBottom; // Bottom of blob in BLN coords. uinT8 YTop; // Top of blob in BLN coords. }; // The standard feature length const double kStandardFeatureLength = 64.0 / 5; /**---------------------------------------------------------------------------- Public Function Prototypes ----------------------------------------------------------------------------**/ void InitIntegerFX(); // Returns a vector representing the direction of a feature with the given // theta direction in an INT_FEATURE_STRUCT. FCOORD FeatureDirection(uinT8 theta); namespace tesseract { // Generates a TrainingSample from a TBLOB. Extracts features and sets // the bounding box, so classifiers that operate on the image can work. // TODO(rays) BlobToTrainingSample must remain a global function until // the FlexFx and FeatureDescription code can be removed and LearnBlob // made a member of Classify. TrainingSample* BlobToTrainingSample( const TBLOB& blob, bool nonlinear_norm, INT_FX_RESULT_STRUCT* fx_info, GenericVector* bl_features); } // Deprecated! Prefer tesseract::Classify::ExtractFeatures instead. bool ExtractIntFeat(const TBLOB& blob, bool nonlinear_norm, INT_FEATURE_ARRAY BLFeat, INT_FEATURE_ARRAY CNFeat, INT_FX_RESULT_STRUCT* Results); #endif tesseract-3.04.01/classify/intmatcher.cpp000066400000000000000000001347141266071204500203500ustar00rootroot00000000000000/****************************************************************************** ** Filename: intmatcher.c ** Purpose: Generic high level classification routines. ** Author: Robert Moss ** History: Wed Feb 13 17:35:28 MST 1991, RWM, Created. ** Mon Mar 11 16:33:02 MST 1991, RWM, Modified to add ** support for adaptive matching. ** (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 automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif /*---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------*/ #include "intmatcher.h" #include "fontinfo.h" #include "intproto.h" #include "callcpp.h" #include "scrollview.h" #include "float2int.h" #include "globals.h" #include "helpers.h" #include "classify.h" #include "shapetable.h" #include using tesseract::ScoredFont; using tesseract::UnicharRating; /*---------------------------------------------------------------------------- Global Data Definitions and Declarations ----------------------------------------------------------------------------*/ // Parameters of the sigmoid used to convert similarity to evidence in the // similarity_evidence_table_ that is used to convert distance metric to an // 8 bit evidence value in the secondary matcher. (See IntMatcher::Init). const float IntegerMatcher::kSEExponentialMultiplier = 0.0; const float IntegerMatcher::kSimilarityCenter = 0.0075; #define offset_table_entries \ 255, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, \ 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, \ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, \ 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, \ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, \ 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, \ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, \ 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, \ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, \ 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, \ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 #define INTMATCHER_OFFSET_TABLE_SIZE 256 #define next_table_entries \ 0, 0, 0, 0x2, 0, 0x4, 0x4, 0x6, 0, 0x8, 0x8, 0x0a, 0x08, 0x0c, 0x0c, 0x0e, \ 0, 0x10, 0x10, 0x12, 0x10, 0x14, 0x14, 0x16, 0x10, 0x18, 0x18, 0x1a, \ 0x18, 0x1c, 0x1c, 0x1e, 0, 0x20, 0x20, 0x22, 0x20, 0x24, 0x24, 0x26, \ 0x20, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x2c, 0x2e, 0x20, 0x30, 0x30, 0x32, \ 0x30, 0x34, 0x34, 0x36, 0x30, 0x38, 0x38, 0x3a, 0x38, 0x3c, 0x3c, 0x3e, \ 0, 0x40, 0x40, 0x42, 0x40, 0x44, 0x44, 0x46, 0x40, 0x48, 0x48, 0x4a, \ 0x48, 0x4c, 0x4c, 0x4e, 0x40, 0x50, 0x50, 0x52, 0x50, 0x54, 0x54, 0x56, \ 0x50, 0x58, 0x58, 0x5a, 0x58, 0x5c, 0x5c, 0x5e, 0x40, 0x60, 0x60, 0x62, \ 0x60, 0x64, 0x64, 0x66, 0x60, 0x68, 0x68, 0x6a, 0x68, 0x6c, 0x6c, 0x6e, \ 0x60, 0x70, 0x70, 0x72, 0x70, 0x74, 0x74, 0x76, 0x70, 0x78, 0x78, 0x7a, \ 0x78, 0x7c, 0x7c, 0x7e, 0, 0x80, 0x80, 0x82, 0x80, 0x84, 0x84, 0x86, \ 0x80, 0x88, 0x88, 0x8a, 0x88, 0x8c, 0x8c, 0x8e, 0x80, 0x90, 0x90, 0x92, \ 0x90, 0x94, 0x94, 0x96, 0x90, 0x98, 0x98, 0x9a, 0x98, 0x9c, 0x9c, 0x9e, \ 0x80, 0xa0, 0xa0, 0xa2, 0xa0, 0xa4, 0xa4, 0xa6, 0xa0, 0xa8, 0xa8, 0xaa, \ 0xa8, 0xac, 0xac, 0xae, 0xa0, 0xb0, 0xb0, 0xb2, 0xb0, 0xb4, 0xb4, 0xb6, \ 0xb0, 0xb8, 0xb8, 0xba, 0xb8, 0xbc, 0xbc, 0xbe, 0x80, 0xc0, 0xc0, 0xc2, \ 0xc0, 0xc4, 0xc4, 0xc6, 0xc0, 0xc8, 0xc8, 0xca, 0xc8, 0xcc, 0xcc, 0xce, \ 0xc0, 0xd0, 0xd0, 0xd2, 0xd0, 0xd4, 0xd4, 0xd6, 0xd0, 0xd8, 0xd8, 0xda, \ 0xd8, 0xdc, 0xdc, 0xde, 0xc0, 0xe0, 0xe0, 0xe2, 0xe0, 0xe4, 0xe4, 0xe6, \ 0xe0, 0xe8, 0xe8, 0xea, 0xe8, 0xec, 0xec, 0xee, 0xe0, 0xf0, 0xf0, 0xf2, \ 0xf0, 0xf4, 0xf4, 0xf6, 0xf0, 0xf8, 0xf8, 0xfa, 0xf8, 0xfc, 0xfc, 0xfe // See http://b/19318793 (#6) for a complete discussion. Merging arrays // offset_table and next_table helps improve performance of PIE code. static const uinT8 data_table[512] = {offset_table_entries, next_table_entries}; static const uinT8* const offset_table = &data_table[0]; static const uinT8* const next_table = &data_table[INTMATCHER_OFFSET_TABLE_SIZE]; namespace tesseract { // Encapsulation of the intermediate data and computations made by the class // pruner. The class pruner implements a simple linear classifier on binary // features by heavily quantizing the feature space, and applying // NUM_BITS_PER_CLASS (2)-bit weights to the features. Lack of resolution in // weights is compensated by a non-constant bias that is dependent on the // number of features present. class ClassPruner { public: ClassPruner(int max_classes) { // The unrolled loop in ComputeScores means that the array sizes need to // be rounded up so that the array is big enough to accommodate the extra // entries accessed by the unrolling. Each pruner word is of sized // BITS_PER_WERD and each entry is NUM_BITS_PER_CLASS, so there are // BITS_PER_WERD / NUM_BITS_PER_CLASS entries. // See ComputeScores. max_classes_ = max_classes; rounded_classes_ = RoundUp( max_classes, WERDS_PER_CP_VECTOR * BITS_PER_WERD / NUM_BITS_PER_CLASS); class_count_ = new int[rounded_classes_]; norm_count_ = new int[rounded_classes_]; sort_key_ = new int[rounded_classes_ + 1]; sort_index_ = new int[rounded_classes_ + 1]; for (int i = 0; i < rounded_classes_; i++) { class_count_[i] = 0; } pruning_threshold_ = 0; num_features_ = 0; num_classes_ = 0; } ~ClassPruner() { delete []class_count_; delete []norm_count_; delete []sort_key_; delete []sort_index_; } /// Computes the scores for every class in the character set, by summing the /// weights for each feature and stores the sums internally in class_count_. void ComputeScores(const INT_TEMPLATES_STRUCT* int_templates, int num_features, const INT_FEATURE_STRUCT* features) { num_features_ = num_features; int num_pruners = int_templates->NumClassPruners; for (int f = 0; f < num_features; ++f) { const INT_FEATURE_STRUCT* feature = &features[f]; // Quantize the feature to NUM_CP_BUCKETS*NUM_CP_BUCKETS*NUM_CP_BUCKETS. int x = feature->X * NUM_CP_BUCKETS >> 8; int y = feature->Y * NUM_CP_BUCKETS >> 8; int theta = feature->Theta * NUM_CP_BUCKETS >> 8; int class_id = 0; // Each CLASS_PRUNER_STRUCT only covers CLASSES_PER_CP(32) classes, so // we need a collection of them, indexed by pruner_set. for (int pruner_set = 0; pruner_set < num_pruners; ++pruner_set) { // Look up quantized feature in a 3-D array, an array of weights for // each class. const uinT32* pruner_word_ptr = int_templates->ClassPruners[pruner_set]->p[x][y][theta]; for (int word = 0; word < WERDS_PER_CP_VECTOR; ++word) { uinT32 pruner_word = *pruner_word_ptr++; // This inner loop is unrolled to speed up the ClassPruner. // Currently gcc would not unroll it unless it is set to O3 // level of optimization or -funroll-loops is specified. /* uinT32 class_mask = (1 << NUM_BITS_PER_CLASS) - 1; for (int bit = 0; bit < BITS_PER_WERD/NUM_BITS_PER_CLASS; bit++) { class_count_[class_id++] += pruner_word & class_mask; pruner_word >>= NUM_BITS_PER_CLASS; } */ class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; pruner_word >>= NUM_BITS_PER_CLASS; class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; pruner_word >>= NUM_BITS_PER_CLASS; class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; pruner_word >>= NUM_BITS_PER_CLASS; class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; pruner_word >>= NUM_BITS_PER_CLASS; class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; pruner_word >>= NUM_BITS_PER_CLASS; class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; pruner_word >>= NUM_BITS_PER_CLASS; class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; pruner_word >>= NUM_BITS_PER_CLASS; class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; pruner_word >>= NUM_BITS_PER_CLASS; class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; pruner_word >>= NUM_BITS_PER_CLASS; class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; pruner_word >>= NUM_BITS_PER_CLASS; class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; pruner_word >>= NUM_BITS_PER_CLASS; class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; pruner_word >>= NUM_BITS_PER_CLASS; class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; pruner_word >>= NUM_BITS_PER_CLASS; class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; pruner_word >>= NUM_BITS_PER_CLASS; class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; pruner_word >>= NUM_BITS_PER_CLASS; class_count_[class_id++] += pruner_word & CLASS_PRUNER_CLASS_MASK; } } } } /// Adjusts the scores according to the number of expected features. Used /// in lieu of a constant bias, this penalizes classes that expect more /// features than there are present. Thus an actual c will score higher for c /// than e, even though almost all the features match e as well as c, because /// e expects more features to be present. void AdjustForExpectedNumFeatures(const uinT16* expected_num_features, int cutoff_strength) { for (int class_id = 0; class_id < max_classes_; ++class_id) { if (num_features_ < expected_num_features[class_id]) { int deficit = expected_num_features[class_id] - num_features_; class_count_[class_id] -= class_count_[class_id] * deficit / (num_features_ * cutoff_strength + deficit); } } } /// Zeros the scores for classes disabled in the unicharset. /// Implements the black-list to recognize a subset of the character set. void DisableDisabledClasses(const UNICHARSET& unicharset) { for (int class_id = 0; class_id < max_classes_; ++class_id) { if (!unicharset.get_enabled(class_id)) class_count_[class_id] = 0; // This char is disabled! } } /** Zeros the scores of fragments. */ void DisableFragments(const UNICHARSET& unicharset) { for (int class_id = 0; class_id < max_classes_; ++class_id) { // Do not include character fragments in the class pruner // results if disable_character_fragments is true. if (unicharset.get_fragment(class_id)) { class_count_[class_id] = 0; } } } /// Normalizes the counts for xheight, putting the normalized result in /// norm_count_. Applies a simple subtractive penalty for incorrect vertical /// position provided by the normalization_factors array, indexed by /// character class, and scaled by the norm_multiplier. void NormalizeForXheight(int norm_multiplier, const uinT8* normalization_factors) { for (int class_id = 0; class_id < max_classes_; class_id++) { norm_count_[class_id] = class_count_[class_id] - ((norm_multiplier * normalization_factors[class_id]) >> 8); } } /** The nop normalization copies the class_count_ array to norm_count_. */ void NoNormalization() { for (int class_id = 0; class_id < max_classes_; class_id++) { norm_count_[class_id] = class_count_[class_id]; } } /// Prunes the classes using <the maximum count> * pruning_factor/256 as a /// threshold for keeping classes. If max_of_non_fragments, then ignore /// fragments in computing the maximum count. void PruneAndSort(int pruning_factor, int keep_this, bool max_of_non_fragments, const UNICHARSET& unicharset) { int max_count = 0; for (int c = 0; c < max_classes_; ++c) { if (norm_count_[c] > max_count && // This additional check is added in order to ensure that // the classifier will return at least one non-fragmented // character match. // TODO(daria): verify that this helps accuracy and does not // hurt performance. (!max_of_non_fragments || !unicharset.get_fragment(c))) { max_count = norm_count_[c]; } } // Prune Classes. pruning_threshold_ = (max_count * pruning_factor) >> 8; // Select Classes. if (pruning_threshold_ < 1) pruning_threshold_ = 1; num_classes_ = 0; for (int class_id = 0; class_id < max_classes_; class_id++) { if (norm_count_[class_id] >= pruning_threshold_ || class_id == keep_this) { ++num_classes_; sort_index_[num_classes_] = class_id; sort_key_[num_classes_] = norm_count_[class_id]; } } // Sort Classes using Heapsort Algorithm. if (num_classes_ > 1) HeapSort(num_classes_, sort_key_, sort_index_); } /** Prints debug info on the class pruner matches for the pruned classes only. */ void DebugMatch(const Classify& classify, const INT_TEMPLATES_STRUCT* int_templates, const INT_FEATURE_STRUCT* features) const { int num_pruners = int_templates->NumClassPruners; int max_num_classes = int_templates->NumClasses; for (int f = 0; f < num_features_; ++f) { const INT_FEATURE_STRUCT* feature = &features[f]; tprintf("F=%3d(%d,%d,%d),", f, feature->X, feature->Y, feature->Theta); // Quantize the feature to NUM_CP_BUCKETS*NUM_CP_BUCKETS*NUM_CP_BUCKETS. int x = feature->X * NUM_CP_BUCKETS >> 8; int y = feature->Y * NUM_CP_BUCKETS >> 8; int theta = feature->Theta * NUM_CP_BUCKETS >> 8; int class_id = 0; for (int pruner_set = 0; pruner_set < num_pruners; ++pruner_set) { // Look up quantized feature in a 3-D array, an array of weights for // each class. const uinT32* pruner_word_ptr = int_templates->ClassPruners[pruner_set]->p[x][y][theta]; for (int word = 0; word < WERDS_PER_CP_VECTOR; ++word) { uinT32 pruner_word = *pruner_word_ptr++; for (int word_class = 0; word_class < 16 && class_id < max_num_classes; ++word_class, ++class_id) { if (norm_count_[class_id] >= pruning_threshold_) { tprintf(" %s=%d,", classify.ClassIDToDebugStr(int_templates, class_id, 0).string(), pruner_word & CLASS_PRUNER_CLASS_MASK); } pruner_word >>= NUM_BITS_PER_CLASS; } } tprintf("\n"); } } } /** Prints a summary of the pruner result. */ void SummarizeResult(const Classify& classify, const INT_TEMPLATES_STRUCT* int_templates, const uinT16* expected_num_features, int norm_multiplier, const uinT8* normalization_factors) const { tprintf("CP:%d classes, %d features:\n", num_classes_, num_features_); for (int i = 0; i < num_classes_; ++i) { int class_id = sort_index_[num_classes_ - i]; STRING class_string = classify.ClassIDToDebugStr(int_templates, class_id, 0); tprintf("%s:Initial=%d, E=%d, Xht-adj=%d, N=%d, Rat=%.2f\n", class_string.string(), class_count_[class_id], expected_num_features[class_id], (norm_multiplier * normalization_factors[class_id]) >> 8, sort_key_[num_classes_ - i], 100.0 - 100.0 * sort_key_[num_classes_ - i] / (CLASS_PRUNER_CLASS_MASK * num_features_)); } } /// Copies the pruned, sorted classes into the output results and returns /// the number of classes. int SetupResults(GenericVector* results) const { CP_RESULT_STRUCT empty; results->init_to_size(num_classes_, empty); for (int c = 0; c < num_classes_; ++c) { (*results)[c].Class = sort_index_[num_classes_ - c]; (*results)[c].Rating = 1.0 - sort_key_[num_classes_ - c] / (static_cast(CLASS_PRUNER_CLASS_MASK) * num_features_); } return num_classes_; } private: /** Array[rounded_classes_] of initial counts for each class. */ int *class_count_; /// Array[rounded_classes_] of modified counts for each class after normalizing /// for expected number of features, disabled classes, fragments, and xheights. int *norm_count_; /** Array[rounded_classes_ +1] of pruned counts that gets sorted */ int *sort_key_; /** Array[rounded_classes_ +1] of classes corresponding to sort_key_. */ int *sort_index_; /** Number of classes in this class pruner. */ int max_classes_; /** Rounded up number of classes used for array sizes. */ int rounded_classes_; /** Threshold count applied to prune classes. */ int pruning_threshold_; /** The number of features used to compute the scores. */ int num_features_; /** Final number of pruned classes. */ int num_classes_; }; /*---------------------------------------------------------------------------- Public Code ----------------------------------------------------------------------------*/ /** * Runs the class pruner from int_templates on the given features, returning * the number of classes output in results. * @param int_templates Class pruner tables * @param num_features Number of features in blob * @param features Array of features * @param normalization_factors Array of fudge factors from blob * normalization process (by CLASS_INDEX) * @param expected_num_features Array of expected number of features * for each class (by CLASS_INDEX) * @param results Sorted Array of pruned classes. Must be an array * of size at least int_templates->NumClasses. * @param keep_this */ int Classify::PruneClasses(const INT_TEMPLATES_STRUCT* int_templates, int num_features, int keep_this, const INT_FEATURE_STRUCT* features, const uinT8* normalization_factors, const uinT16* expected_num_features, GenericVector* results) { ClassPruner pruner(int_templates->NumClasses); // Compute initial match scores for all classes. pruner.ComputeScores(int_templates, num_features, features); // Adjust match scores for number of expected features. pruner.AdjustForExpectedNumFeatures(expected_num_features, classify_cp_cutoff_strength); // Apply disabled classes in unicharset - only works without a shape_table. if (shape_table_ == NULL) pruner.DisableDisabledClasses(unicharset); // If fragments are disabled, remove them, also only without a shape table. if (disable_character_fragments && shape_table_ == NULL) pruner.DisableFragments(unicharset); // If we have good x-heights, apply the given normalization factors. if (normalization_factors != NULL) { pruner.NormalizeForXheight(classify_class_pruner_multiplier, normalization_factors); } else { pruner.NoNormalization(); } // Do the actual pruning and sort the short-list. pruner.PruneAndSort(classify_class_pruner_threshold, keep_this, shape_table_ == NULL, unicharset); if (classify_debug_level > 2) { pruner.DebugMatch(*this, int_templates, features); } if (classify_debug_level > 1) { pruner.SummarizeResult(*this, int_templates, expected_num_features, classify_class_pruner_multiplier, normalization_factors); } // Convert to the expected output format. return pruner.SetupResults(results); } } // namespace tesseract /** * IntegerMatcher returns the best configuration and rating * for a single class. The class matched against is determined * by the uniqueness of the ClassTemplate parameter. The * best rating and its associated configuration are returned. * * Globals: * - local_matcher_multiplier_ Normalization factor multiplier * param ClassTemplate Prototypes & tables for a class * param BlobLength Length of unormalized blob * param NumFeatures Number of features in blob * param Features Array of features * param NormalizationFactor Fudge factor from blob normalization process * param Result Class rating & configuration: (0.0 -> 1.0), 0=bad, 1=good * param Debug Debugger flag: 1=debugger on * @return none * @note Exceptions: none * @note History: Tue Feb 19 16:36:23 MST 1991, RWM, Created. */ void IntegerMatcher::Match(INT_CLASS ClassTemplate, BIT_VECTOR ProtoMask, BIT_VECTOR ConfigMask, inT16 NumFeatures, const INT_FEATURE_STRUCT* Features, UnicharRating* Result, int AdaptFeatureThreshold, int Debug, bool SeparateDebugWindows) { ScratchEvidence *tables = new ScratchEvidence(); int Feature; int BestMatch; if (MatchDebuggingOn (Debug)) cprintf ("Integer Matcher -------------------------------------------\n"); tables->Clear(ClassTemplate); Result->feature_misses = 0; for (Feature = 0; Feature < NumFeatures; Feature++) { int csum = UpdateTablesForFeature(ClassTemplate, ProtoMask, ConfigMask, Feature, &Features[Feature], tables, Debug); // Count features that were missed over all configs. if (csum == 0) ++Result->feature_misses; } #ifndef GRAPHICS_DISABLED if (PrintProtoMatchesOn(Debug) || PrintMatchSummaryOn(Debug)) { DebugFeatureProtoError(ClassTemplate, ProtoMask, ConfigMask, *tables, NumFeatures, Debug); } if (DisplayProtoMatchesOn(Debug)) { DisplayProtoDebugInfo(ClassTemplate, ProtoMask, ConfigMask, *tables, SeparateDebugWindows); } if (DisplayFeatureMatchesOn(Debug)) { DisplayFeatureDebugInfo(ClassTemplate, ProtoMask, ConfigMask, NumFeatures, Features, AdaptFeatureThreshold, Debug, SeparateDebugWindows); } #endif tables->UpdateSumOfProtoEvidences(ClassTemplate, ConfigMask, NumFeatures); tables->NormalizeSums(ClassTemplate, NumFeatures, NumFeatures); BestMatch = FindBestMatch(ClassTemplate, *tables, Result); #ifndef GRAPHICS_DISABLED if (PrintMatchSummaryOn(Debug)) Result->Print(); if (MatchDebuggingOn(Debug)) cprintf("Match Complete --------------------------------------------\n"); #endif delete tables; } /** * FindGoodProtos finds all protos whose normalized proto-evidence * exceed classify_adapt_proto_thresh. The list is ordered by increasing * proto id number. * * Globals: * - local_matcher_multiplier_ Normalization factor multiplier * param ClassTemplate Prototypes & tables for a class * param ProtoMask AND Mask for proto word * param ConfigMask AND Mask for config word * param BlobLength Length of unormalized blob * param NumFeatures Number of features in blob * param Features Array of features * param ProtoArray Array of good protos * param AdaptProtoThreshold Threshold for good protos * param Debug Debugger flag: 1=debugger on * @return Number of good protos in ProtoArray. * @note Exceptions: none * @note History: Tue Mar 12 17:09:26 MST 1991, RWM, Created */ int IntegerMatcher::FindGoodProtos( INT_CLASS ClassTemplate, BIT_VECTOR ProtoMask, BIT_VECTOR ConfigMask, uinT16 BlobLength, inT16 NumFeatures, INT_FEATURE_ARRAY Features, PROTO_ID *ProtoArray, int AdaptProtoThreshold, int Debug) { ScratchEvidence *tables = new ScratchEvidence(); int NumGoodProtos = 0; /* DEBUG opening heading */ if (MatchDebuggingOn (Debug)) cprintf ("Find Good Protos -------------------------------------------\n"); tables->Clear(ClassTemplate); for (int Feature = 0; Feature < NumFeatures; Feature++) UpdateTablesForFeature( ClassTemplate, ProtoMask, ConfigMask, Feature, &(Features[Feature]), tables, Debug); #ifndef GRAPHICS_DISABLED if (PrintProtoMatchesOn (Debug) || PrintMatchSummaryOn (Debug)) DebugFeatureProtoError(ClassTemplate, ProtoMask, ConfigMask, *tables, NumFeatures, Debug); #endif /* Average Proto Evidences & Find Good Protos */ for (int proto = 0; proto < ClassTemplate->NumProtos; proto++) { /* Compute Average for Actual Proto */ int Temp = 0; for (int i = 0; i < ClassTemplate->ProtoLengths[proto]; i++) Temp += tables->proto_evidence_[proto][i]; Temp /= ClassTemplate->ProtoLengths[proto]; /* Find Good Protos */ if (Temp >= AdaptProtoThreshold) { *ProtoArray = proto; ProtoArray++; NumGoodProtos++; } } if (MatchDebuggingOn (Debug)) cprintf ("Match Complete --------------------------------------------\n"); delete tables; return NumGoodProtos; } /** * FindBadFeatures finds all features with maximum feature-evidence < * AdaptFeatureThresh. The list is ordered by increasing feature number. * @param ClassTemplate Prototypes & tables for a class * @param ProtoMask AND Mask for proto word * @param ConfigMask AND Mask for config word * @param BlobLength Length of unormalized blob * @param NumFeatures Number of features in blob * @param Features Array of features * @param FeatureArray Array of bad features * @param AdaptFeatureThreshold Threshold for bad features * @param Debug Debugger flag: 1=debugger on * @return Number of bad features in FeatureArray. * @note History: Tue Mar 12 17:09:26 MST 1991, RWM, Created */ int IntegerMatcher::FindBadFeatures( INT_CLASS ClassTemplate, BIT_VECTOR ProtoMask, BIT_VECTOR ConfigMask, uinT16 BlobLength, inT16 NumFeatures, INT_FEATURE_ARRAY Features, FEATURE_ID *FeatureArray, int AdaptFeatureThreshold, int Debug) { ScratchEvidence *tables = new ScratchEvidence(); int NumBadFeatures = 0; /* DEBUG opening heading */ if (MatchDebuggingOn(Debug)) cprintf("Find Bad Features -------------------------------------------\n"); tables->Clear(ClassTemplate); for (int Feature = 0; Feature < NumFeatures; Feature++) { UpdateTablesForFeature( ClassTemplate, ProtoMask, ConfigMask, Feature, &Features[Feature], tables, Debug); /* Find Best Evidence for Current Feature */ int best = 0; for (int i = 0; i < ClassTemplate->NumConfigs; i++) if (tables->feature_evidence_[i] > best) best = tables->feature_evidence_[i]; /* Find Bad Features */ if (best < AdaptFeatureThreshold) { *FeatureArray = Feature; FeatureArray++; NumBadFeatures++; } } #ifndef GRAPHICS_DISABLED if (PrintProtoMatchesOn(Debug) || PrintMatchSummaryOn(Debug)) DebugFeatureProtoError(ClassTemplate, ProtoMask, ConfigMask, *tables, NumFeatures, Debug); #endif if (MatchDebuggingOn(Debug)) cprintf("Match Complete --------------------------------------------\n"); delete tables; return NumBadFeatures; } void IntegerMatcher::Init(tesseract::IntParam *classify_debug_level) { classify_debug_level_ = classify_debug_level; /* Initialize table for evidence to similarity lookup */ for (int i = 0; i < SE_TABLE_SIZE; i++) { uinT32 IntSimilarity = i << (27 - SE_TABLE_BITS); double Similarity = ((double) IntSimilarity) / 65536.0 / 65536.0; double evidence = Similarity / kSimilarityCenter; evidence = 255.0 / (evidence * evidence + 1.0); if (kSEExponentialMultiplier > 0.0) { double scale = 1.0 - exp(-kSEExponentialMultiplier) * exp(kSEExponentialMultiplier * ((double) i / SE_TABLE_SIZE)); evidence *= ClipToRange(scale, 0.0, 1.0); } similarity_evidence_table_[i] = (uinT8) (evidence + 0.5); } /* Initialize evidence computation variables */ evidence_table_mask_ = ((1 << kEvidenceTableBits) - 1) << (9 - kEvidenceTableBits); mult_trunc_shift_bits_ = (14 - kIntEvidenceTruncBits); table_trunc_shift_bits_ = (27 - SE_TABLE_BITS - (mult_trunc_shift_bits_ << 1)); evidence_mult_mask_ = ((1 << kIntEvidenceTruncBits) - 1); } /*---------------------------------------------------------------------------- Private Code ----------------------------------------------------------------------------*/ void ScratchEvidence::Clear(const INT_CLASS class_template) { memset(sum_feature_evidence_, 0, class_template->NumConfigs * sizeof(sum_feature_evidence_[0])); memset(proto_evidence_, 0, class_template->NumProtos * sizeof(proto_evidence_[0])); } void ScratchEvidence::ClearFeatureEvidence(const INT_CLASS class_template) { memset(feature_evidence_, 0, class_template->NumConfigs * sizeof(feature_evidence_[0])); } /** * Print debugging information for Configuations * @return none * @note Exceptions: none * @note History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. */ void IMDebugConfiguration(int FeatureNum, uinT16 ActualProtoNum, uinT8 Evidence, BIT_VECTOR ConfigMask, uinT32 ConfigWord) { cprintf ("F = %3d, P = %3d, E = %3d, Configs = ", FeatureNum, (int) ActualProtoNum, (int) Evidence); while (ConfigWord) { if (ConfigWord & 1) cprintf ("1"); else cprintf ("0"); ConfigWord >>= 1; } cprintf ("\n"); } /** * Print debugging information for Configuations * @return none * @note Exceptions: none * @note History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. */ void IMDebugConfigurationSum(int FeatureNum, uinT8 *FeatureEvidence, inT32 ConfigCount) { cprintf("F=%3d, C=", FeatureNum); for (int ConfigNum = 0; ConfigNum < ConfigCount; ConfigNum++) { cprintf("%4d", FeatureEvidence[ConfigNum]); } cprintf("\n"); } /** * For the given feature: prune protos, compute evidence, * update Feature Evidence, Proto Evidence, and Sum of Feature * Evidence tables. * @param ClassTemplate Prototypes & tables for a class * @param FeatureNum Current feature number (for DEBUG only) * @param Feature Pointer to a feature struct * @param tables Evidence tables * @param Debug Debugger flag: 1=debugger on * @return none */ int IntegerMatcher::UpdateTablesForFeature( INT_CLASS ClassTemplate, BIT_VECTOR ProtoMask, BIT_VECTOR ConfigMask, int FeatureNum, const INT_FEATURE_STRUCT* Feature, ScratchEvidence *tables, int Debug) { uinT32 ConfigWord; uinT32 ProtoWord; uinT32 ProtoNum; uinT32 ActualProtoNum; uinT8 proto_byte; inT32 proto_word_offset; inT32 proto_offset; uinT8 config_byte; inT32 config_offset; PROTO_SET ProtoSet; uinT32 *ProtoPrunerPtr; INT_PROTO Proto; int ProtoSetIndex; uinT8 Evidence; uinT32 XFeatureAddress; uinT32 YFeatureAddress; uinT32 ThetaFeatureAddress; uinT8 *UINT8Pointer; int ProtoIndex; uinT8 Temp; int *IntPointer; int ConfigNum; inT32 M3; inT32 A3; uinT32 A4; tables->ClearFeatureEvidence(ClassTemplate); /* Precompute Feature Address offset for Proto Pruning */ XFeatureAddress = ((Feature->X >> 2) << 1); YFeatureAddress = (NUM_PP_BUCKETS << 1) + ((Feature->Y >> 2) << 1); ThetaFeatureAddress = (NUM_PP_BUCKETS << 2) + ((Feature->Theta >> 2) << 1); for (ProtoSetIndex = 0, ActualProtoNum = 0; ProtoSetIndex < ClassTemplate->NumProtoSets; ProtoSetIndex++) { ProtoSet = ClassTemplate->ProtoSets[ProtoSetIndex]; ProtoPrunerPtr = (uinT32 *) ((*ProtoSet).ProtoPruner); for (ProtoNum = 0; ProtoNum < PROTOS_PER_PROTO_SET; ProtoNum += (PROTOS_PER_PROTO_SET >> 1), ActualProtoNum += (PROTOS_PER_PROTO_SET >> 1), ProtoMask++, ProtoPrunerPtr++) { /* Prune Protos of current Proto Set */ ProtoWord = *(ProtoPrunerPtr + XFeatureAddress); ProtoWord &= *(ProtoPrunerPtr + YFeatureAddress); ProtoWord &= *(ProtoPrunerPtr + ThetaFeatureAddress); ProtoWord &= *ProtoMask; if (ProtoWord != 0) { proto_byte = ProtoWord & 0xff; ProtoWord >>= 8; proto_word_offset = 0; while (ProtoWord != 0 || proto_byte != 0) { while (proto_byte == 0) { proto_byte = ProtoWord & 0xff; ProtoWord >>= 8; proto_word_offset += 8; } proto_offset = offset_table[proto_byte] + proto_word_offset; proto_byte = next_table[proto_byte]; Proto = &(ProtoSet->Protos[ProtoNum + proto_offset]); ConfigWord = Proto->Configs[0]; A3 = (((Proto->A * (Feature->X - 128)) << 1) - (Proto->B * (Feature->Y - 128)) + (Proto->C << 9)); M3 = (((inT8) (Feature->Theta - Proto->Angle)) * kIntThetaFudge) << 1; if (A3 < 0) A3 = ~A3; if (M3 < 0) M3 = ~M3; A3 >>= mult_trunc_shift_bits_; M3 >>= mult_trunc_shift_bits_; if (A3 > evidence_mult_mask_) A3 = evidence_mult_mask_; if (M3 > evidence_mult_mask_) M3 = evidence_mult_mask_; A4 = (A3 * A3) + (M3 * M3); A4 >>= table_trunc_shift_bits_; if (A4 > evidence_table_mask_) Evidence = 0; else Evidence = similarity_evidence_table_[A4]; if (PrintFeatureMatchesOn (Debug)) IMDebugConfiguration (FeatureNum, ActualProtoNum + proto_offset, Evidence, ConfigMask, ConfigWord); ConfigWord &= *ConfigMask; UINT8Pointer = tables->feature_evidence_ - 8; config_byte = 0; while (ConfigWord != 0 || config_byte != 0) { while (config_byte == 0) { config_byte = ConfigWord & 0xff; ConfigWord >>= 8; UINT8Pointer += 8; } config_offset = offset_table[config_byte]; config_byte = next_table[config_byte]; if (Evidence > UINT8Pointer[config_offset]) UINT8Pointer[config_offset] = Evidence; } UINT8Pointer = &(tables->proto_evidence_[ActualProtoNum + proto_offset][0]); for (ProtoIndex = ClassTemplate->ProtoLengths[ActualProtoNum + proto_offset]; ProtoIndex > 0; ProtoIndex--, UINT8Pointer++) { if (Evidence > *UINT8Pointer) { Temp = *UINT8Pointer; *UINT8Pointer = Evidence; Evidence = Temp; } else if (Evidence == 0) break; } } } } } if (PrintFeatureMatchesOn(Debug)) { IMDebugConfigurationSum(FeatureNum, tables->feature_evidence_, ClassTemplate->NumConfigs); } IntPointer = tables->sum_feature_evidence_; UINT8Pointer = tables->feature_evidence_; int SumOverConfigs = 0; for (ConfigNum = ClassTemplate->NumConfigs; ConfigNum > 0; ConfigNum--) { int evidence = *UINT8Pointer++; SumOverConfigs += evidence; *IntPointer++ += evidence; } return SumOverConfigs; } /** * Print debugging information for Configuations * @return none * @note Exceptions: none * @note History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. */ #ifndef GRAPHICS_DISABLED void IntegerMatcher::DebugFeatureProtoError( INT_CLASS ClassTemplate, BIT_VECTOR ProtoMask, BIT_VECTOR ConfigMask, const ScratchEvidence& tables, inT16 NumFeatures, int Debug) { FLOAT32 ProtoConfigs[MAX_NUM_CONFIGS]; int ConfigNum; uinT32 ConfigWord; int ProtoSetIndex; uinT16 ProtoNum; uinT8 ProtoWordNum; PROTO_SET ProtoSet; uinT16 ActualProtoNum; if (PrintMatchSummaryOn(Debug)) { cprintf("Configuration Mask:\n"); for (ConfigNum = 0; ConfigNum < ClassTemplate->NumConfigs; ConfigNum++) cprintf("%1d", (((*ConfigMask) >> ConfigNum) & 1)); cprintf("\n"); cprintf("Feature Error for Configurations:\n"); for (ConfigNum = 0; ConfigNum < ClassTemplate->NumConfigs; ConfigNum++) { cprintf( " %5.1f", 100.0 * (1.0 - (FLOAT32) tables.sum_feature_evidence_[ConfigNum] / NumFeatures / 256.0)); } cprintf("\n\n\n"); } if (PrintMatchSummaryOn (Debug)) { cprintf ("Proto Mask:\n"); for (ProtoSetIndex = 0; ProtoSetIndex < ClassTemplate->NumProtoSets; ProtoSetIndex++) { ActualProtoNum = (ProtoSetIndex * PROTOS_PER_PROTO_SET); for (ProtoWordNum = 0; ProtoWordNum < 2; ProtoWordNum++, ProtoMask++) { ActualProtoNum = (ProtoSetIndex * PROTOS_PER_PROTO_SET); for (ProtoNum = 0; ((ProtoNum < (PROTOS_PER_PROTO_SET >> 1)) && (ActualProtoNum < ClassTemplate->NumProtos)); ProtoNum++, ActualProtoNum++) cprintf ("%1d", (((*ProtoMask) >> ProtoNum) & 1)); cprintf ("\n"); } } cprintf ("\n"); } for (int i = 0; i < ClassTemplate->NumConfigs; i++) ProtoConfigs[i] = 0; if (PrintProtoMatchesOn (Debug)) { cprintf ("Proto Evidence:\n"); for (ProtoSetIndex = 0; ProtoSetIndex < ClassTemplate->NumProtoSets; ProtoSetIndex++) { ProtoSet = ClassTemplate->ProtoSets[ProtoSetIndex]; ActualProtoNum = (ProtoSetIndex * PROTOS_PER_PROTO_SET); for (ProtoNum = 0; ((ProtoNum < PROTOS_PER_PROTO_SET) && (ActualProtoNum < ClassTemplate->NumProtos)); ProtoNum++, ActualProtoNum++) { cprintf ("P %3d =", ActualProtoNum); int temp = 0; for (int j = 0; j < ClassTemplate->ProtoLengths[ActualProtoNum]; j++) { uinT8 data = tables.proto_evidence_[ActualProtoNum][j]; cprintf(" %d", data); temp += data; } cprintf(" = %6.4f%%\n", temp / 256.0 / ClassTemplate->ProtoLengths[ActualProtoNum]); ConfigWord = ProtoSet->Protos[ProtoNum].Configs[0]; ConfigNum = 0; while (ConfigWord) { cprintf ("%5d", ConfigWord & 1 ? temp : 0); if (ConfigWord & 1) ProtoConfigs[ConfigNum] += temp; ConfigNum++; ConfigWord >>= 1; } cprintf("\n"); } } } if (PrintMatchSummaryOn (Debug)) { cprintf ("Proto Error for Configurations:\n"); for (ConfigNum = 0; ConfigNum < ClassTemplate->NumConfigs; ConfigNum++) cprintf (" %5.1f", 100.0 * (1.0 - ProtoConfigs[ConfigNum] / ClassTemplate->ConfigLengths[ConfigNum] / 256.0)); cprintf ("\n\n"); } if (PrintProtoMatchesOn (Debug)) { cprintf ("Proto Sum for Configurations:\n"); for (ConfigNum = 0; ConfigNum < ClassTemplate->NumConfigs; ConfigNum++) cprintf (" %4.1f", ProtoConfigs[ConfigNum] / 256.0); cprintf ("\n\n"); cprintf ("Proto Length for Configurations:\n"); for (ConfigNum = 0; ConfigNum < ClassTemplate->NumConfigs; ConfigNum++) cprintf (" %4.1f", (float) ClassTemplate->ConfigLengths[ConfigNum]); cprintf ("\n\n"); } } void IntegerMatcher::DisplayProtoDebugInfo( INT_CLASS ClassTemplate, BIT_VECTOR ProtoMask, BIT_VECTOR ConfigMask, const ScratchEvidence& tables, bool SeparateDebugWindows) { uinT16 ProtoNum; uinT16 ActualProtoNum; PROTO_SET ProtoSet; int ProtoSetIndex; InitIntMatchWindowIfReqd(); if (SeparateDebugWindows) { InitFeatureDisplayWindowIfReqd(); InitProtoDisplayWindowIfReqd(); } for (ProtoSetIndex = 0; ProtoSetIndex < ClassTemplate->NumProtoSets; ProtoSetIndex++) { ProtoSet = ClassTemplate->ProtoSets[ProtoSetIndex]; ActualProtoNum = ProtoSetIndex * PROTOS_PER_PROTO_SET; for (ProtoNum = 0; ((ProtoNum < PROTOS_PER_PROTO_SET) && (ActualProtoNum < ClassTemplate->NumProtos)); ProtoNum++, ActualProtoNum++) { /* Compute Average for Actual Proto */ int temp = 0; for (int i = 0; i < ClassTemplate->ProtoLengths[ActualProtoNum]; i++) temp += tables.proto_evidence_[ActualProtoNum][i]; temp /= ClassTemplate->ProtoLengths[ActualProtoNum]; if ((ProtoSet->Protos[ProtoNum]).Configs[0] & (*ConfigMask)) { DisplayIntProto(ClassTemplate, ActualProtoNum, temp / 255.0); } } } } void IntegerMatcher::DisplayFeatureDebugInfo( INT_CLASS ClassTemplate, BIT_VECTOR ProtoMask, BIT_VECTOR ConfigMask, inT16 NumFeatures, const INT_FEATURE_STRUCT* Features, int AdaptFeatureThreshold, int Debug, bool SeparateDebugWindows) { ScratchEvidence *tables = new ScratchEvidence(); tables->Clear(ClassTemplate); InitIntMatchWindowIfReqd(); if (SeparateDebugWindows) { InitFeatureDisplayWindowIfReqd(); InitProtoDisplayWindowIfReqd(); } for (int Feature = 0; Feature < NumFeatures; Feature++) { UpdateTablesForFeature( ClassTemplate, ProtoMask, ConfigMask, Feature, &Features[Feature], tables, 0); /* Find Best Evidence for Current Feature */ int best = 0; for (int i = 0; i < ClassTemplate->NumConfigs; i++) if (tables->feature_evidence_[i] > best) best = tables->feature_evidence_[i]; /* Update display for current feature */ if (ClipMatchEvidenceOn(Debug)) { if (best < AdaptFeatureThreshold) DisplayIntFeature(&Features[Feature], 0.0); else DisplayIntFeature(&Features[Feature], 1.0); } else { DisplayIntFeature(&Features[Feature], best / 255.0); } } delete tables; } #endif /** * Add sum of Proto Evidences into Sum Of Feature Evidence Array */ void ScratchEvidence::UpdateSumOfProtoEvidences( INT_CLASS ClassTemplate, BIT_VECTOR ConfigMask, inT16 NumFeatures) { int *IntPointer; uinT32 ConfigWord; int ProtoSetIndex; uinT16 ProtoNum; PROTO_SET ProtoSet; int NumProtos; uinT16 ActualProtoNum; NumProtos = ClassTemplate->NumProtos; for (ProtoSetIndex = 0; ProtoSetIndex < ClassTemplate->NumProtoSets; ProtoSetIndex++) { ProtoSet = ClassTemplate->ProtoSets[ProtoSetIndex]; ActualProtoNum = (ProtoSetIndex * PROTOS_PER_PROTO_SET); for (ProtoNum = 0; ((ProtoNum < PROTOS_PER_PROTO_SET) && (ActualProtoNum < NumProtos)); ProtoNum++, ActualProtoNum++) { int temp = 0; for (int i = 0; i < ClassTemplate->ProtoLengths[ActualProtoNum]; i++) temp += proto_evidence_[ActualProtoNum] [i]; ConfigWord = ProtoSet->Protos[ProtoNum].Configs[0]; ConfigWord &= *ConfigMask; IntPointer = sum_feature_evidence_; while (ConfigWord) { if (ConfigWord & 1) *IntPointer += temp; IntPointer++; ConfigWord >>= 1; } } } } /** * Normalize Sum of Proto and Feature Evidence by dividing by the sum of * the Feature Lengths and the Proto Lengths for each configuration. */ void ScratchEvidence::NormalizeSums( INT_CLASS ClassTemplate, inT16 NumFeatures, inT32 used_features) { for (int i = 0; i < ClassTemplate->NumConfigs; i++) { sum_feature_evidence_[i] = (sum_feature_evidence_[i] << 8) / (NumFeatures + ClassTemplate->ConfigLengths[i]); } } /** * Find the best match for the current class and update the Result * with the configuration and match rating. * @return The best normalized sum of evidences * @note Exceptions: none * @note History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. */ int IntegerMatcher::FindBestMatch( INT_CLASS class_template, const ScratchEvidence &tables, UnicharRating* result) { int best_match = 0; result->config = 0; result->fonts.truncate(0); result->fonts.reserve(class_template->NumConfigs); /* Find best match */ for (int c = 0; c < class_template->NumConfigs; ++c) { int rating = tables.sum_feature_evidence_[c]; if (*classify_debug_level_ > 2) tprintf("Config %d, rating=%d\n", c, rating); if (rating > best_match) { result->config = c; best_match = rating; } result->fonts.push_back(ScoredFont(c, rating)); } // Compute confidence on a Probability scale. result->rating = best_match / 65536.0f; return best_match; } /** * Applies the CN normalization factor to the given rating and returns * the modified rating. */ float IntegerMatcher::ApplyCNCorrection(float rating, int blob_length, int normalization_factor, int matcher_multiplier) { return (rating * blob_length + matcher_multiplier * normalization_factor / 256.0) / (blob_length + matcher_multiplier); } /** * Sort Key array in ascending order using heap sort * algorithm. Also sort Index array that is tied to * the key array. * @param n Number of elements to sort * @param ra Key array [1..n] * @param rb Index array [1..n] * @return none * @note Exceptions: none * @note History: Tue Feb 19 10:24:24 MST 1991, RWM, Created. */ void HeapSort (int n, register int ra[], register int rb[]) { int i, rra, rrb; int l, j, ir; l = (n >> 1) + 1; ir = n; for (;;) { if (l > 1) { rra = ra[--l]; rrb = rb[l]; } else { rra = ra[ir]; rrb = rb[ir]; ra[ir] = ra[1]; rb[ir] = rb[1]; if (--ir == 1) { ra[1] = rra; rb[1] = rrb; return; } } i = l; j = l << 1; while (j <= ir) { if (j < ir && ra[j] < ra[j + 1]) ++j; if (rra < ra[j]) { ra[i] = ra[j]; rb[i] = rb[j]; j += (i = j); } else j = ir + 1; } ra[i] = rra; rb[i] = rrb; } } tesseract-3.04.01/classify/intmatcher.h000066400000000000000000000161721266071204500200120ustar00rootroot00000000000000/****************************************************************************** ** Filename: intmatcher.h ** Purpose: Interface to high level generic classifier routines. ** Author: Robert Moss ** History: Wed Feb 13 15:24:15 MST 1991, RWM, 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 INTMATCHER_H #define INTMATCHER_H #include "params.h" // Character fragments could be present in the trained templaes // but turned on/off on the language-by-language basis or depending // on particular properties of the corpus (e.g. when we expect the // images to have low exposure). extern BOOL_VAR_H(disable_character_fragments, FALSE, "Do not include character fragments in the" " results of the classifier"); extern INT_VAR_H(classify_integer_matcher_multiplier, 10, "Integer Matcher Multiplier 0-255: "); /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "intproto.h" #include "cutoffs.h" namespace tesseract { struct UnicharRating; } struct CP_RESULT_STRUCT { CP_RESULT_STRUCT() : Rating(0.0f), Class(0) {} FLOAT32 Rating; CLASS_ID Class; }; /*---------------------------------------------------------------------------- Variables -----------------------------------------------------------------------------*/ extern INT_VAR_H(classify_adapt_proto_thresh, 230, "Threshold for good protos during adaptive 0-255: "); extern INT_VAR_H(classify_adapt_feature_thresh, 230, "Threshold for good features during adaptive 0-255: "); /**---------------------------------------------------------------------------- Public Function Prototypes ----------------------------------------------------------------------------**/ #define SE_TABLE_BITS 9 #define SE_TABLE_SIZE 512 struct ScratchEvidence { uinT8 feature_evidence_[MAX_NUM_CONFIGS]; int sum_feature_evidence_[MAX_NUM_CONFIGS]; uinT8 proto_evidence_[MAX_NUM_PROTOS][MAX_PROTO_INDEX]; void Clear(const INT_CLASS class_template); void ClearFeatureEvidence(const INT_CLASS class_template); void NormalizeSums(INT_CLASS ClassTemplate, inT16 NumFeatures, inT32 used_features); void UpdateSumOfProtoEvidences( INT_CLASS ClassTemplate, BIT_VECTOR ConfigMask, inT16 NumFeatures); }; class IntegerMatcher { public: // Integer Matcher Theta Fudge (0-255). static const int kIntThetaFudge = 128; // Bits in Similarity to Evidence Lookup (8-9). static const int kEvidenceTableBits = 9; // Integer Evidence Truncation Bits (8-14). static const int kIntEvidenceTruncBits = 14; // Similarity to Evidence Table Exponential Multiplier. static const float kSEExponentialMultiplier; // Center of Similarity Curve. static const float kSimilarityCenter; IntegerMatcher() : classify_debug_level_(0) {} void Init(tesseract::IntParam *classify_debug_level); void Match(INT_CLASS ClassTemplate, BIT_VECTOR ProtoMask, BIT_VECTOR ConfigMask, inT16 NumFeatures, const INT_FEATURE_STRUCT* Features, tesseract::UnicharRating* Result, int AdaptFeatureThreshold, int Debug, bool SeparateDebugWindows); // Applies the CN normalization factor to the given rating and returns // the modified rating. float ApplyCNCorrection(float rating, int blob_length, int normalization_factor, int matcher_multiplier); int FindGoodProtos(INT_CLASS ClassTemplate, BIT_VECTOR ProtoMask, BIT_VECTOR ConfigMask, uinT16 BlobLength, inT16 NumFeatures, INT_FEATURE_ARRAY Features, PROTO_ID *ProtoArray, int AdaptProtoThreshold, int Debug); int FindBadFeatures(INT_CLASS ClassTemplate, BIT_VECTOR ProtoMask, BIT_VECTOR ConfigMask, uinT16 BlobLength, inT16 NumFeatures, INT_FEATURE_ARRAY Features, FEATURE_ID *FeatureArray, int AdaptFeatureThreshold, int Debug); private: int UpdateTablesForFeature( INT_CLASS ClassTemplate, BIT_VECTOR ProtoMask, BIT_VECTOR ConfigMask, int FeatureNum, const INT_FEATURE_STRUCT* Feature, ScratchEvidence *evidence, int Debug); int FindBestMatch(INT_CLASS ClassTemplate, const ScratchEvidence &tables, tesseract::UnicharRating* Result); #ifndef GRAPHICS_DISABLED void DebugFeatureProtoError( INT_CLASS ClassTemplate, BIT_VECTOR ProtoMask, BIT_VECTOR ConfigMask, const ScratchEvidence &tables, inT16 NumFeatures, int Debug); void DisplayProtoDebugInfo( INT_CLASS ClassTemplate, BIT_VECTOR ProtoMask, BIT_VECTOR ConfigMask, const ScratchEvidence &tables, bool SeparateDebugWindows); void DisplayFeatureDebugInfo( INT_CLASS ClassTemplate, BIT_VECTOR ProtoMask, BIT_VECTOR ConfigMask, inT16 NumFeatures, const INT_FEATURE_STRUCT* Features, int AdaptFeatureThreshold, int Debug, bool SeparateDebugWindows); #endif private: uinT8 similarity_evidence_table_[SE_TABLE_SIZE]; uinT32 evidence_table_mask_; uinT32 mult_trunc_shift_bits_; uinT32 table_trunc_shift_bits_; tesseract::IntParam *classify_debug_level_; uinT32 evidence_mult_mask_; }; /**---------------------------------------------------------------------------- Private Function Prototypes ----------------------------------------------------------------------------**/ void IMDebugConfiguration(INT_FEATURE FeatureNum, uinT16 ActualProtoNum, uinT8 Evidence, BIT_VECTOR ConfigMask, uinT32 ConfigWord); void IMDebugConfigurationSum(INT_FEATURE FeatureNum, uinT8 *FeatureEvidence, inT32 ConfigCount); void HeapSort (int n, register int ra[], register int rb[]); /**---------------------------------------------------------------------------- Global Data Definitions and Declarations ----------------------------------------------------------------------------**/ #endif tesseract-3.04.01/classify/intproto.cpp000066400000000000000000002036301266071204500200620ustar00rootroot00000000000000/****************************************************************************** ** Filename: intproto.c ** Purpose: Definition of data structures for integer protos. ** Author: Dan Johnson ** History: Thu Feb 7 14:38:16 1991, 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 #include #include #ifdef __UNIX__ #include #endif #include "classify.h" #include "const.h" #include "emalloc.h" #include "fontinfo.h" #include "genericvector.h" #include "globals.h" #include "helpers.h" #include "intproto.h" #include "mfoutline.h" #include "ndminx.h" #include "picofeat.h" #include "points.h" #include "shapetable.h" #include "svmnode.h" // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif using tesseract::FontInfo; using tesseract::FontSet; using tesseract::FontSpacingInfo; /* match debug display constants*/ #define PROTO_PRUNER_SCALE (4.0) #define INT_DESCENDER (0.0 * INT_CHAR_NORM_RANGE) #define INT_BASELINE (0.25 * INT_CHAR_NORM_RANGE) #define INT_XHEIGHT (0.75 * INT_CHAR_NORM_RANGE) #define INT_CAPHEIGHT (1.0 * INT_CHAR_NORM_RANGE) #define INT_XCENTER (0.5 * INT_CHAR_NORM_RANGE) #define INT_YCENTER (0.5 * INT_CHAR_NORM_RANGE) #define INT_XRADIUS (0.2 * INT_CHAR_NORM_RANGE) #define INT_YRADIUS (0.2 * INT_CHAR_NORM_RANGE) #define INT_MIN_X 0 #define INT_MIN_Y 0 #define INT_MAX_X INT_CHAR_NORM_RANGE #define INT_MAX_Y INT_CHAR_NORM_RANGE /** define pad used to snap near horiz/vertical protos to horiz/vertical */ #define HV_TOLERANCE (0.0025) /* approx 0.9 degrees */ typedef enum { StartSwitch, EndSwitch, LastSwitch } SWITCH_TYPE; #define MAX_NUM_SWITCHES 3 typedef struct { SWITCH_TYPE Type; inT8 X, Y; inT16 YInit; inT16 Delta; } FILL_SWITCH; typedef struct { uinT8 NextSwitch; uinT8 AngleStart, AngleEnd; inT8 X; inT16 YStart, YEnd; inT16 StartDelta, EndDelta; FILL_SWITCH Switch[MAX_NUM_SWITCHES]; } TABLE_FILLER; typedef struct { inT8 X; inT8 YStart, YEnd; uinT8 AngleStart, AngleEnd; } FILL_SPEC; /* constants for conversion from old inttemp format */ #define OLD_MAX_NUM_CONFIGS 32 #define OLD_WERDS_PER_CONFIG_VEC ((OLD_MAX_NUM_CONFIGS + BITS_PER_WERD - 1) /\ BITS_PER_WERD) /*----------------------------------------------------------------------------- Macros -----------------------------------------------------------------------------*/ /** macro for performing circular increments of bucket indices */ #define CircularIncrement(i,r) (((i) < (r) - 1)?((i)++):((i) = 0)) /** macro for mapping floats to ints without bounds checking */ #define MapParam(P,O,N) (floor (((P) + (O)) * (N))) /*--------------------------------------------------------------------------- Private Function Prototypes ----------------------------------------------------------------------------*/ FLOAT32 BucketStart(int Bucket, FLOAT32 Offset, int NumBuckets); FLOAT32 BucketEnd(int Bucket, FLOAT32 Offset, int NumBuckets); void DoFill(FILL_SPEC *FillSpec, CLASS_PRUNER_STRUCT* Pruner, register uinT32 ClassMask, register uinT32 ClassCount, register uinT32 WordIndex); BOOL8 FillerDone(TABLE_FILLER *Filler); void FillPPCircularBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug); void FillPPLinearBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug); void GetCPPadsForLevel(int Level, FLOAT32 *EndPad, FLOAT32 *SidePad, FLOAT32 *AnglePad); ScrollView::Color GetMatchColorFor(FLOAT32 Evidence); void GetNextFill(TABLE_FILLER *Filler, FILL_SPEC *Fill); void InitTableFiller(FLOAT32 EndPad, FLOAT32 SidePad, FLOAT32 AnglePad, PROTO Proto, TABLE_FILLER *Filler); #ifndef GRAPHICS_DISABLED void RenderIntFeature(ScrollView *window, const INT_FEATURE_STRUCT* Feature, ScrollView::Color color); void RenderIntProto(ScrollView *window, INT_CLASS Class, PROTO_ID ProtoId, ScrollView::Color color); #endif // GRAPHICS_DISABLED int TruncateParam(FLOAT32 Param, int Min, int Max, char *Id); /*----------------------------------------------------------------------------- Global Data Definitions and Declarations -----------------------------------------------------------------------------*/ /* global display lists used to display proto and feature match information*/ ScrollView *IntMatchWindow = NULL; ScrollView *FeatureDisplayWindow = NULL; ScrollView *ProtoDisplayWindow = NULL; /*----------------------------------------------------------------------------- Variables -----------------------------------------------------------------------------*/ /* control knobs */ INT_VAR(classify_num_cp_levels, 3, "Number of Class Pruner Levels"); double_VAR(classify_cp_angle_pad_loose, 45.0, "Class Pruner Angle Pad Loose"); double_VAR(classify_cp_angle_pad_medium, 20.0, "Class Pruner Angle Pad Medium"); double_VAR(classify_cp_angle_pad_tight, 10.0, "CLass Pruner Angle Pad Tight"); double_VAR(classify_cp_end_pad_loose, 0.5, "Class Pruner End Pad Loose"); double_VAR(classify_cp_end_pad_medium, 0.5, "Class Pruner End Pad Medium"); double_VAR(classify_cp_end_pad_tight, 0.5, "Class Pruner End Pad Tight"); double_VAR(classify_cp_side_pad_loose, 2.5, "Class Pruner Side Pad Loose"); double_VAR(classify_cp_side_pad_medium, 1.2, "Class Pruner Side Pad Medium"); double_VAR(classify_cp_side_pad_tight, 0.6, "Class Pruner Side Pad Tight"); double_VAR(classify_pp_angle_pad, 45.0, "Proto Pruner Angle Pad"); double_VAR(classify_pp_end_pad, 0.5, "Proto Prune End Pad"); double_VAR(classify_pp_side_pad, 2.5, "Proto Pruner Side Pad"); /*----------------------------------------------------------------------------- Public Code -----------------------------------------------------------------------------*/ /// Builds a feature from an FCOORD for position with all the necessary /// clipping and rounding. INT_FEATURE_STRUCT::INT_FEATURE_STRUCT(const FCOORD& pos, uinT8 theta) : X(ClipToRange(static_cast(pos.x() + 0.5), 0, 255)), Y(ClipToRange(static_cast(pos.y() + 0.5), 0, 255)), Theta(theta), CP_misses(0) { } /** Builds a feature from ints with all the necessary clipping and casting. */ INT_FEATURE_STRUCT::INT_FEATURE_STRUCT(int x, int y, int theta) : X(static_cast(ClipToRange(x, 0, MAX_UINT8))), Y(static_cast(ClipToRange(y, 0, MAX_UINT8))), Theta(static_cast(ClipToRange(theta, 0, MAX_UINT8))), CP_misses(0) { } /** * This routine adds a new class structure to a set of * templates. Classes have to be added to Templates in * the order of increasing ClassIds. * * @param Templates templates to add new class to * @param ClassId class id to associate new class with * @param Class class data structure to add to templates * * Globals: none * * @note Exceptions: none * @note History: Mon Feb 11 11:52:08 1991, DSJ, Created. */ void AddIntClass(INT_TEMPLATES Templates, CLASS_ID ClassId, INT_CLASS Class) { int Pruner; assert (LegalClassId (ClassId)); if (ClassId != Templates->NumClasses) { fprintf(stderr, "Please make sure that classes are added to templates"); fprintf(stderr, " in increasing order of ClassIds\n"); exit(1); } ClassForClassId (Templates, ClassId) = Class; Templates->NumClasses++; if (Templates->NumClasses > MaxNumClassesIn (Templates)) { Pruner = Templates->NumClassPruners++; Templates->ClassPruners[Pruner] = new CLASS_PRUNER_STRUCT; memset(Templates->ClassPruners[Pruner], 0, sizeof(CLASS_PRUNER_STRUCT)); } } /* AddIntClass */ /** * This routine returns the index of the next free config * in Class. * * @param Class class to add new configuration to * * Globals: none * * @return Index of next free config. * @note Exceptions: none * @note History: Mon Feb 11 14:44:40 1991, DSJ, Created. */ int AddIntConfig(INT_CLASS Class) { int Index; assert(Class->NumConfigs < MAX_NUM_CONFIGS); Index = Class->NumConfigs++; Class->ConfigLengths[Index] = 0; return Index; } /* AddIntConfig */ /** * This routine allocates the next free proto in Class and * returns its index. * * @param Class class to add new proto to * * Globals: none * * @return Proto index of new proto. * @note Exceptions: none * @note History: Mon Feb 11 13:26:41 1991, DSJ, Created. */ int AddIntProto(INT_CLASS Class) { int Index; int ProtoSetId; PROTO_SET ProtoSet; INT_PROTO Proto; uinT32 *Word; if (Class->NumProtos >= MAX_NUM_PROTOS) return (NO_PROTO); Index = Class->NumProtos++; if (Class->NumProtos > MaxNumIntProtosIn(Class)) { ProtoSetId = Class->NumProtoSets++; ProtoSet = (PROTO_SET) Emalloc(sizeof(PROTO_SET_STRUCT)); Class->ProtoSets[ProtoSetId] = ProtoSet; memset(ProtoSet, 0, sizeof(*ProtoSet)); /* reallocate space for the proto lengths and install in class */ Class->ProtoLengths = (uinT8 *)Erealloc(Class->ProtoLengths, MaxNumIntProtosIn(Class) * sizeof(uinT8)); memset(&Class->ProtoLengths[Index], 0, sizeof(*Class->ProtoLengths) * (MaxNumIntProtosIn(Class) - Index)); } /* initialize proto so its length is zero and it isn't in any configs */ Class->ProtoLengths[Index] = 0; Proto = ProtoForProtoId (Class, Index); for (Word = Proto->Configs; Word < Proto->Configs + WERDS_PER_CONFIG_VEC; *Word++ = 0); return (Index); } /** * This routine adds Proto to the class pruning tables * for the specified class in Templates. * * Globals: * - classify_num_cp_levels number of levels used in the class pruner * @param Proto floating-pt proto to add to class pruner * @param ClassId class id corresponding to Proto * @param Templates set of templates containing class pruner * @return none * @note Exceptions: none * @note History: Wed Feb 13 08:49:54 1991, DSJ, Created. */ void AddProtoToClassPruner (PROTO Proto, CLASS_ID ClassId, INT_TEMPLATES Templates) #define MAX_LEVEL 2 { CLASS_PRUNER_STRUCT* Pruner; uinT32 ClassMask; uinT32 ClassCount; uinT32 WordIndex; int Level; FLOAT32 EndPad, SidePad, AnglePad; TABLE_FILLER TableFiller; FILL_SPEC FillSpec; Pruner = CPrunerFor (Templates, ClassId); WordIndex = CPrunerWordIndexFor (ClassId); ClassMask = CPrunerMaskFor (MAX_LEVEL, ClassId); for (Level = classify_num_cp_levels - 1; Level >= 0; Level--) { GetCPPadsForLevel(Level, &EndPad, &SidePad, &AnglePad); ClassCount = CPrunerMaskFor (Level, ClassId); InitTableFiller(EndPad, SidePad, AnglePad, Proto, &TableFiller); while (!FillerDone (&TableFiller)) { GetNextFill(&TableFiller, &FillSpec); DoFill(&FillSpec, Pruner, ClassMask, ClassCount, WordIndex); } } } /* AddProtoToClassPruner */ /** * This routine updates the proto pruner lookup tables * for Class to include a new proto identified by ProtoId * and described by Proto. * @param Proto floating-pt proto to be added to proto pruner * @param ProtoId id of proto * @param Class integer class that contains desired proto pruner * @param debug debug flag * @note Globals: none * @return none * @note Exceptions: none * @note History: Fri Feb 8 13:07:19 1991, DSJ, Created. */ void AddProtoToProtoPruner(PROTO Proto, int ProtoId, INT_CLASS Class, bool debug) { FLOAT32 Angle, X, Y, Length; FLOAT32 Pad; int Index; PROTO_SET ProtoSet; if (ProtoId >= Class->NumProtos) cprintf("AddProtoToProtoPruner:assert failed: %d < %d", ProtoId, Class->NumProtos); assert(ProtoId < Class->NumProtos); Index = IndexForProto (ProtoId); ProtoSet = Class->ProtoSets[SetForProto (ProtoId)]; Angle = Proto->Angle; #ifndef _WIN32 assert(!isnan(Angle)); #endif FillPPCircularBits (ProtoSet->ProtoPruner[PRUNER_ANGLE], Index, Angle + ANGLE_SHIFT, classify_pp_angle_pad / 360.0, debug); Angle *= 2.0 * PI; Length = Proto->Length; X = Proto->X + X_SHIFT; Pad = MAX (fabs (cos (Angle)) * (Length / 2.0 + classify_pp_end_pad * GetPicoFeatureLength ()), fabs (sin (Angle)) * (classify_pp_side_pad * GetPicoFeatureLength ())); FillPPLinearBits(ProtoSet->ProtoPruner[PRUNER_X], Index, X, Pad, debug); Y = Proto->Y + Y_SHIFT; Pad = MAX (fabs (sin (Angle)) * (Length / 2.0 + classify_pp_end_pad * GetPicoFeatureLength ()), fabs (cos (Angle)) * (classify_pp_side_pad * GetPicoFeatureLength ())); FillPPLinearBits(ProtoSet->ProtoPruner[PRUNER_Y], Index, Y, Pad, debug); } /* AddProtoToProtoPruner */ /** * Returns a quantized bucket for the given param shifted by offset, * notionally (param + offset) * num_buckets, but clipped and casted to the * appropriate type. */ uinT8 Bucket8For(FLOAT32 param, FLOAT32 offset, int num_buckets) { int bucket = IntCastRounded(MapParam(param, offset, num_buckets)); return static_cast(ClipToRange(bucket, 0, num_buckets - 1)); } uinT16 Bucket16For(FLOAT32 param, FLOAT32 offset, int num_buckets) { int bucket = IntCastRounded(MapParam(param, offset, num_buckets)); return static_cast(ClipToRange(bucket, 0, num_buckets - 1)); } /** * Returns a quantized bucket for the given circular param shifted by offset, * notionally (param + offset) * num_buckets, but modded and casted to the * appropriate type. */ uinT8 CircBucketFor(FLOAT32 param, FLOAT32 offset, int num_buckets) { int bucket = IntCastRounded(MapParam(param, offset, num_buckets)); return static_cast(Modulo(bucket, num_buckets)); } /* CircBucketFor */ #ifndef GRAPHICS_DISABLED /** * This routine clears the global feature and proto * display lists. * * Globals: * - FeatureShapes display list for features * - ProtoShapes display list for protos * @return none * @note Exceptions: none * @note History: Thu Mar 21 15:40:19 1991, DSJ, Created. */ void UpdateMatchDisplay() { if (IntMatchWindow != NULL) IntMatchWindow->Update(); } /* ClearMatchDisplay */ #endif /** * This operation updates the config vectors of all protos * in Class to indicate that the protos with 1's in Config * belong to a new configuration identified by ConfigId. * It is assumed that the length of the Config bit vector is * equal to the number of protos in Class. * @param Config config to be added to class * @param ConfigId id to be used for new config * @param Class class to add new config to * @return none * @note Globals: none * @note Exceptions: none * @note History: Mon Feb 11 14:57:31 1991, DSJ, Created. */ void ConvertConfig(BIT_VECTOR Config, int ConfigId, INT_CLASS Class) { int ProtoId; INT_PROTO Proto; int TotalLength; for (ProtoId = 0, TotalLength = 0; ProtoId < Class->NumProtos; ProtoId++) { if (test_bit(Config, ProtoId)) { Proto = ProtoForProtoId(Class, ProtoId); SET_BIT(Proto->Configs, ConfigId); TotalLength += Class->ProtoLengths[ProtoId]; } } Class->ConfigLengths[ConfigId] = TotalLength; } /* ConvertConfig */ namespace tesseract { /** * This routine converts Proto to integer format and * installs it as ProtoId in Class. * @param Proto floating-pt proto to be converted to integer format * @param ProtoId id of proto * @param Class integer class to add converted proto to * @return none * @note Globals: none * @note Exceptions: none * @note History: Fri Feb 8 11:22:43 1991, DSJ, Created. */ void Classify::ConvertProto(PROTO Proto, int ProtoId, INT_CLASS Class) { INT_PROTO P; FLOAT32 Param; assert(ProtoId < Class->NumProtos); P = ProtoForProtoId(Class, ProtoId); Param = Proto->A * 128; P->A = TruncateParam(Param, -128, 127, NULL); Param = -Proto->B * 256; P->B = TruncateParam(Param, 0, 255, NULL); Param = Proto->C * 128; P->C = TruncateParam(Param, -128, 127, NULL); Param = Proto->Angle * 256; if (Param < 0 || Param >= 256) P->Angle = 0; else P->Angle = (uinT8) Param; /* round proto length to nearest integer number of pico-features */ Param = (Proto->Length / GetPicoFeatureLength()) + 0.5; Class->ProtoLengths[ProtoId] = TruncateParam(Param, 1, 255, NULL); if (classify_learning_debug_level >= 2) cprintf("Converted ffeat to (A=%d,B=%d,C=%d,L=%d)", P->A, P->B, P->C, Class->ProtoLengths[ProtoId]); } /* ConvertProto */ /** * This routine converts from the old floating point format * to the new integer format. * @param FloatProtos prototypes in old floating pt format * @param target_unicharset the UNICHARSET to use * @return New set of training templates in integer format. * @note Globals: none * @note Exceptions: none * @note History: Thu Feb 7 14:40:42 1991, DSJ, Created. */ INT_TEMPLATES Classify::CreateIntTemplates(CLASSES FloatProtos, const UNICHARSET& target_unicharset) { INT_TEMPLATES IntTemplates; CLASS_TYPE FClass; INT_CLASS IClass; int ClassId; int ProtoId; int ConfigId; IntTemplates = NewIntTemplates(); for (ClassId = 0; ClassId < target_unicharset.size(); ClassId++) { FClass = &(FloatProtos[ClassId]); if (FClass->NumProtos == 0 && FClass->NumConfigs == 0 && strcmp(target_unicharset.id_to_unichar(ClassId), " ") != 0) { cprintf("Warning: no protos/configs for %s in CreateIntTemplates()\n", target_unicharset.id_to_unichar(ClassId)); } assert(UnusedClassIdIn(IntTemplates, ClassId)); IClass = NewIntClass(FClass->NumProtos, FClass->NumConfigs); FontSet fs; fs.size = FClass->font_set.size(); fs.configs = new int[fs.size]; for (int i = 0; i < fs.size; ++i) { fs.configs[i] = FClass->font_set.get(i); } if (this->fontset_table_.contains(fs)) { IClass->font_set_id = this->fontset_table_.get_id(fs); delete[] fs.configs; } else { IClass->font_set_id = this->fontset_table_.push_back(fs); } AddIntClass(IntTemplates, ClassId, IClass); for (ProtoId = 0; ProtoId < FClass->NumProtos; ProtoId++) { AddIntProto(IClass); ConvertProto(ProtoIn(FClass, ProtoId), ProtoId, IClass); AddProtoToProtoPruner(ProtoIn(FClass, ProtoId), ProtoId, IClass, classify_learning_debug_level >= 2); AddProtoToClassPruner(ProtoIn(FClass, ProtoId), ClassId, IntTemplates); } for (ConfigId = 0; ConfigId < FClass->NumConfigs; ConfigId++) { AddIntConfig(IClass); ConvertConfig(FClass->Configurations[ConfigId], ConfigId, IClass); } } return (IntTemplates); } /* CreateIntTemplates */ } // namespace tesseract #ifndef GRAPHICS_DISABLED /** * This routine renders the specified feature into a * global display list. * * Globals: * - FeatureShapes global display list for features * @param Feature pico-feature to be displayed * @param Evidence best evidence for this feature (0-1) * @return none * @note Exceptions: none * @note History: Thu Mar 21 14:45:04 1991, DSJ, Created. */ void DisplayIntFeature(const INT_FEATURE_STRUCT* Feature, FLOAT32 Evidence) { ScrollView::Color color = GetMatchColorFor(Evidence); RenderIntFeature(IntMatchWindow, Feature, color); if (FeatureDisplayWindow) { RenderIntFeature(FeatureDisplayWindow, Feature, color); } } /* DisplayIntFeature */ /** * This routine renders the specified proto into a * global display list. * * Globals: * - ProtoShapes global display list for protos * @param Class class to take proto from * @param ProtoId id of proto in Class to be displayed * @param Evidence total evidence for proto (0-1) * @return none * @note Exceptions: none * @note History: Thu Mar 21 14:45:04 1991, DSJ, Created. */ void DisplayIntProto(INT_CLASS Class, PROTO_ID ProtoId, FLOAT32 Evidence) { ScrollView::Color color = GetMatchColorFor(Evidence); RenderIntProto(IntMatchWindow, Class, ProtoId, color); if (ProtoDisplayWindow) { RenderIntProto(ProtoDisplayWindow, Class, ProtoId, color); } } /* DisplayIntProto */ #endif /** * This routine creates a new integer class data structure * and returns it. Sufficient space is allocated * to handle the specified number of protos and configs. * @param MaxNumProtos number of protos to allocate space for * @param MaxNumConfigs number of configs to allocate space for * @return New class created. * @note Globals: none * @note Exceptions: none * @note History: Fri Feb 8 10:51:23 1991, DSJ, Created. */ INT_CLASS NewIntClass(int MaxNumProtos, int MaxNumConfigs) { INT_CLASS Class; PROTO_SET ProtoSet; int i; assert(MaxNumConfigs <= MAX_NUM_CONFIGS); Class = (INT_CLASS) Emalloc(sizeof(INT_CLASS_STRUCT)); Class->NumProtoSets = ((MaxNumProtos + PROTOS_PER_PROTO_SET - 1) / PROTOS_PER_PROTO_SET); assert(Class->NumProtoSets <= MAX_NUM_PROTO_SETS); Class->NumProtos = 0; Class->NumConfigs = 0; for (i = 0; i < Class->NumProtoSets; i++) { /* allocate space for a proto set, install in class, and initialize */ ProtoSet = (PROTO_SET) Emalloc(sizeof(PROTO_SET_STRUCT)); memset(ProtoSet, 0, sizeof(*ProtoSet)); Class->ProtoSets[i] = ProtoSet; /* allocate space for the proto lengths and install in class */ } if (MaxNumIntProtosIn (Class) > 0) { Class->ProtoLengths = (uinT8 *)Emalloc(MaxNumIntProtosIn (Class) * sizeof (uinT8)); memset(Class->ProtoLengths, 0, MaxNumIntProtosIn(Class) * sizeof(*Class->ProtoLengths)); } else { Class->ProtoLengths = NULL; } memset(Class->ConfigLengths, 0, sizeof(Class->ConfigLengths)); return (Class); } /* NewIntClass */ void free_int_class(INT_CLASS int_class) { int i; for (i = 0; i < int_class->NumProtoSets; i++) { Efree (int_class->ProtoSets[i]); } if (int_class->ProtoLengths != NULL) { Efree (int_class->ProtoLengths); } Efree(int_class); } /** * This routine allocates a new set of integer templates * initialized to hold 0 classes. * @return The integer templates created. * @note Globals: none * @note Exceptions: none * @note History: Fri Feb 8 08:38:51 1991, DSJ, Created. */ INT_TEMPLATES NewIntTemplates() { INT_TEMPLATES T; int i; T = (INT_TEMPLATES) Emalloc (sizeof (INT_TEMPLATES_STRUCT)); T->NumClasses = 0; T->NumClassPruners = 0; for (i = 0; i < MAX_NUM_CLASSES; i++) ClassForClassId (T, i) = NULL; return (T); } /* NewIntTemplates */ /*---------------------------------------------------------------------------*/ void free_int_templates(INT_TEMPLATES templates) { int i; for (i = 0; i < templates->NumClasses; i++) free_int_class(templates->Class[i]); for (i = 0; i < templates->NumClassPruners; i++) delete templates->ClassPruners[i]; Efree(templates); } namespace tesseract { /** * This routine reads a set of integer templates from * File. File must already be open and must be in the * correct binary format. * @param File open file to read templates from * @return Pointer to integer templates read from File. * @note Globals: none * @note Exceptions: none * @note History: Wed Feb 27 11:48:46 1991, DSJ, Created. */ INT_TEMPLATES Classify::ReadIntTemplates(FILE *File) { int i, j, w, x, y, z; BOOL8 swap; int nread; int unicharset_size; int version_id = 0; INT_TEMPLATES Templates; CLASS_PRUNER_STRUCT* Pruner; INT_CLASS Class; uinT8 *Lengths; PROTO_SET ProtoSet; /* variables for conversion from older inttemp formats */ int b, bit_number, last_cp_bit_number, new_b, new_i, new_w; CLASS_ID class_id, max_class_id; inT16 *IndexFor = new inT16[MAX_NUM_CLASSES]; CLASS_ID *ClassIdFor = new CLASS_ID[MAX_NUM_CLASSES]; CLASS_PRUNER_STRUCT **TempClassPruner = new CLASS_PRUNER_STRUCT*[MAX_NUM_CLASS_PRUNERS]; uinT32 SetBitsForMask = // word with NUM_BITS_PER_CLASS (1 << NUM_BITS_PER_CLASS) - 1; // set starting at bit 0 uinT32 Mask, NewMask, ClassBits; int MaxNumConfigs = MAX_NUM_CONFIGS; int WerdsPerConfigVec = WERDS_PER_CONFIG_VEC; /* first read the high level template struct */ Templates = NewIntTemplates(); // Read Templates in parts for 64 bit compatibility. if (fread(&unicharset_size, sizeof(int), 1, File) != 1) cprintf("Bad read of inttemp!\n"); if (fread(&Templates->NumClasses, sizeof(Templates->NumClasses), 1, File) != 1 || fread(&Templates->NumClassPruners, sizeof(Templates->NumClassPruners), 1, File) != 1) cprintf("Bad read of inttemp!\n"); // Swap status is determined automatically. swap = Templates->NumClassPruners < 0 || Templates->NumClassPruners > MAX_NUM_CLASS_PRUNERS; if (swap) { Reverse32(&Templates->NumClassPruners); Reverse32(&Templates->NumClasses); Reverse32(&unicharset_size); } if (Templates->NumClasses < 0) { // This file has a version id! version_id = -Templates->NumClasses; if (fread(&Templates->NumClasses, sizeof(Templates->NumClasses), 1, File) != 1) cprintf("Bad read of inttemp!\n"); if (swap) Reverse32(&Templates->NumClasses); } if (version_id < 3) { MaxNumConfigs = OLD_MAX_NUM_CONFIGS; WerdsPerConfigVec = OLD_WERDS_PER_CONFIG_VEC; } if (version_id < 2) { for (i = 0; i < unicharset_size; ++i) { if (fread(&IndexFor[i], sizeof(inT16), 1, File) != 1) cprintf("Bad read of inttemp!\n"); } for (i = 0; i < Templates->NumClasses; ++i) { if (fread(&ClassIdFor[i], sizeof(CLASS_ID), 1, File) != 1) cprintf("Bad read of inttemp!\n"); } if (swap) { for (i = 0; i < Templates->NumClasses; i++) Reverse16(&IndexFor[i]); for (i = 0; i < Templates->NumClasses; i++) Reverse32(&ClassIdFor[i]); } } /* then read in the class pruners */ for (i = 0; i < Templates->NumClassPruners; i++) { Pruner = new CLASS_PRUNER_STRUCT; if ((nread = fread(Pruner, 1, sizeof(CLASS_PRUNER_STRUCT), File)) != sizeof(CLASS_PRUNER_STRUCT)) cprintf("Bad read of inttemp!\n"); if (swap) { for (x = 0; x < NUM_CP_BUCKETS; x++) { for (y = 0; y < NUM_CP_BUCKETS; y++) { for (z = 0; z < NUM_CP_BUCKETS; z++) { for (w = 0; w < WERDS_PER_CP_VECTOR; w++) { Reverse32(&Pruner->p[x][y][z][w]); } } } } } if (version_id < 2) { TempClassPruner[i] = Pruner; } else { Templates->ClassPruners[i] = Pruner; } } /* fix class pruners if they came from an old version of inttemp */ if (version_id < 2) { // Allocate enough class pruners to cover all the class ids. max_class_id = 0; for (i = 0; i < Templates->NumClasses; i++) if (ClassIdFor[i] > max_class_id) max_class_id = ClassIdFor[i]; for (i = 0; i <= CPrunerIdFor(max_class_id); i++) { Templates->ClassPruners[i] = new CLASS_PRUNER_STRUCT; memset(Templates->ClassPruners[i], 0, sizeof(CLASS_PRUNER_STRUCT)); } // Convert class pruners from the old format (indexed by class index) // to the new format (indexed by class id). last_cp_bit_number = NUM_BITS_PER_CLASS * Templates->NumClasses - 1; for (i = 0; i < Templates->NumClassPruners; i++) { for (x = 0; x < NUM_CP_BUCKETS; x++) for (y = 0; y < NUM_CP_BUCKETS; y++) for (z = 0; z < NUM_CP_BUCKETS; z++) for (w = 0; w < WERDS_PER_CP_VECTOR; w++) { if (TempClassPruner[i]->p[x][y][z][w] == 0) continue; for (b = 0; b < BITS_PER_WERD; b += NUM_BITS_PER_CLASS) { bit_number = i * BITS_PER_CP_VECTOR + w * BITS_PER_WERD + b; if (bit_number > last_cp_bit_number) break; // the rest of the bits in this word are not used class_id = ClassIdFor[bit_number / NUM_BITS_PER_CLASS]; // Single out NUM_BITS_PER_CLASS bits relating to class_id. Mask = SetBitsForMask << b; ClassBits = TempClassPruner[i]->p[x][y][z][w] & Mask; // Move these bits to the new position in which they should // appear (indexed corresponding to the class_id). new_i = CPrunerIdFor(class_id); new_w = CPrunerWordIndexFor(class_id); new_b = CPrunerBitIndexFor(class_id) * NUM_BITS_PER_CLASS; if (new_b > b) { ClassBits <<= (new_b - b); } else { ClassBits >>= (b - new_b); } // Copy bits relating to class_id to the correct position // in Templates->ClassPruner. NewMask = SetBitsForMask << new_b; Templates->ClassPruners[new_i]->p[x][y][z][new_w] &= ~NewMask; Templates->ClassPruners[new_i]->p[x][y][z][new_w] |= ClassBits; } } } for (i = 0; i < Templates->NumClassPruners; i++) { delete TempClassPruner[i]; } } /* then read in each class */ for (i = 0; i < Templates->NumClasses; i++) { /* first read in the high level struct for the class */ Class = (INT_CLASS) Emalloc (sizeof (INT_CLASS_STRUCT)); if (fread(&Class->NumProtos, sizeof(Class->NumProtos), 1, File) != 1 || fread(&Class->NumProtoSets, sizeof(Class->NumProtoSets), 1, File) != 1 || fread(&Class->NumConfigs, sizeof(Class->NumConfigs), 1, File) != 1) cprintf ("Bad read of inttemp!\n"); if (version_id == 0) { // Only version 0 writes 5 pointless pointers to the file. for (j = 0; j < 5; ++j) { int junk; if (fread(&junk, sizeof(junk), 1, File) != 1) cprintf ("Bad read of inttemp!\n"); } } if (version_id < 4) { for (j = 0; j < MaxNumConfigs; ++j) { if (fread(&Class->ConfigLengths[j], sizeof(uinT16), 1, File) != 1) cprintf ("Bad read of inttemp!\n"); } if (swap) { Reverse16(&Class->NumProtos); for (j = 0; j < MaxNumConfigs; j++) Reverse16(&Class->ConfigLengths[j]); } } else { ASSERT_HOST(Class->NumConfigs < MaxNumConfigs); for (j = 0; j < Class->NumConfigs; ++j) { if (fread(&Class->ConfigLengths[j], sizeof(uinT16), 1, File) != 1) cprintf ("Bad read of inttemp!\n"); } if (swap) { Reverse16(&Class->NumProtos); for (j = 0; j < MaxNumConfigs; j++) Reverse16(&Class->ConfigLengths[j]); } } if (version_id < 2) { ClassForClassId (Templates, ClassIdFor[i]) = Class; } else { ClassForClassId (Templates, i) = Class; } /* then read in the proto lengths */ Lengths = NULL; if (MaxNumIntProtosIn (Class) > 0) { Lengths = (uinT8 *)Emalloc(sizeof(uinT8) * MaxNumIntProtosIn(Class)); if ((nread = fread((char *)Lengths, sizeof(uinT8), MaxNumIntProtosIn(Class), File)) != MaxNumIntProtosIn (Class)) cprintf ("Bad read of inttemp!\n"); } Class->ProtoLengths = Lengths; /* then read in the proto sets */ for (j = 0; j < Class->NumProtoSets; j++) { ProtoSet = (PROTO_SET)Emalloc(sizeof(PROTO_SET_STRUCT)); if (version_id < 3) { if ((nread = fread((char *) &ProtoSet->ProtoPruner, 1, sizeof(PROTO_PRUNER), File)) != sizeof(PROTO_PRUNER)) cprintf("Bad read of inttemp!\n"); for (x = 0; x < PROTOS_PER_PROTO_SET; x++) { if ((nread = fread((char *) &ProtoSet->Protos[x].A, 1, sizeof(inT8), File)) != sizeof(inT8) || (nread = fread((char *) &ProtoSet->Protos[x].B, 1, sizeof(uinT8), File)) != sizeof(uinT8) || (nread = fread((char *) &ProtoSet->Protos[x].C, 1, sizeof(inT8), File)) != sizeof(inT8) || (nread = fread((char *) &ProtoSet->Protos[x].Angle, 1, sizeof(uinT8), File)) != sizeof(uinT8)) cprintf("Bad read of inttemp!\n"); for (y = 0; y < WerdsPerConfigVec; y++) if ((nread = fread((char *) &ProtoSet->Protos[x].Configs[y], 1, sizeof(uinT32), File)) != sizeof(uinT32)) cprintf("Bad read of inttemp!\n"); } } else { if ((nread = fread((char *) ProtoSet, 1, sizeof(PROTO_SET_STRUCT), File)) != sizeof(PROTO_SET_STRUCT)) cprintf("Bad read of inttemp!\n"); } if (swap) { for (x = 0; x < NUM_PP_PARAMS; x++) for (y = 0; y < NUM_PP_BUCKETS; y++) for (z = 0; z < WERDS_PER_PP_VECTOR; z++) Reverse32(&ProtoSet->ProtoPruner[x][y][z]); for (x = 0; x < PROTOS_PER_PROTO_SET; x++) for (y = 0; y < WerdsPerConfigVec; y++) Reverse32(&ProtoSet->Protos[x].Configs[y]); } Class->ProtoSets[j] = ProtoSet; } if (version_id < 4) Class->font_set_id = -1; else { fread(&Class->font_set_id, sizeof(int), 1, File); if (swap) Reverse32(&Class->font_set_id); } } if (version_id < 2) { /* add an empty NULL class with class id 0 */ assert(UnusedClassIdIn (Templates, 0)); ClassForClassId (Templates, 0) = NewIntClass (1, 1); ClassForClassId (Templates, 0)->font_set_id = -1; Templates->NumClasses++; /* make sure the classes are contiguous */ for (i = 0; i < MAX_NUM_CLASSES; i++) { if (i < Templates->NumClasses) { if (ClassForClassId (Templates, i) == NULL) { fprintf(stderr, "Non-contiguous class ids in inttemp\n"); exit(1); } } else { if (ClassForClassId (Templates, i) != NULL) { fprintf(stderr, "Class id %d exceeds NumClassesIn (Templates) %d\n", i, Templates->NumClasses); exit(1); } } } } if (version_id >= 4) { this->fontinfo_table_.read(File, NewPermanentTessCallback(read_info), swap); if (version_id >= 5) { this->fontinfo_table_.read(File, NewPermanentTessCallback(read_spacing_info), swap); } this->fontset_table_.read(File, NewPermanentTessCallback(read_set), swap); } // Clean up. delete[] IndexFor; delete[] ClassIdFor; delete[] TempClassPruner; return (Templates); } /* ReadIntTemplates */ #ifndef GRAPHICS_DISABLED /** * This routine sends the shapes in the global display * lists to the match debugger window. * * Globals: * - FeatureShapes display list containing feature matches * - ProtoShapes display list containing proto matches * @return none * @note Exceptions: none * @note History: Thu Mar 21 15:47:33 1991, DSJ, Created. */ void Classify::ShowMatchDisplay() { InitIntMatchWindowIfReqd(); if (ProtoDisplayWindow) { ProtoDisplayWindow->Clear(); } if (FeatureDisplayWindow) { FeatureDisplayWindow->Clear(); } ClearFeatureSpaceWindow( static_cast(static_cast(classify_norm_method)), IntMatchWindow); IntMatchWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y, INT_MAX_X, INT_MAX_Y); if (ProtoDisplayWindow) { ProtoDisplayWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y, INT_MAX_X, INT_MAX_Y); } if (FeatureDisplayWindow) { FeatureDisplayWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y, INT_MAX_X, INT_MAX_Y); } } /* ShowMatchDisplay */ /// Clears the given window and draws the featurespace guides for the /// appropriate normalization method. void ClearFeatureSpaceWindow(NORM_METHOD norm_method, ScrollView* window) { window->Clear(); window->Pen(ScrollView::GREY); // Draw the feature space limit rectangle. window->Rectangle(0, 0, INT_MAX_X, INT_MAX_Y); if (norm_method == baseline) { window->SetCursor(0, INT_DESCENDER); window->DrawTo(INT_MAX_X, INT_DESCENDER); window->SetCursor(0, INT_BASELINE); window->DrawTo(INT_MAX_X, INT_BASELINE); window->SetCursor(0, INT_XHEIGHT); window->DrawTo(INT_MAX_X, INT_XHEIGHT); window->SetCursor(0, INT_CAPHEIGHT); window->DrawTo(INT_MAX_X, INT_CAPHEIGHT); } else { window->Rectangle(INT_XCENTER - INT_XRADIUS, INT_YCENTER - INT_YRADIUS, INT_XCENTER + INT_XRADIUS, INT_YCENTER + INT_YRADIUS); } } #endif /** * This routine writes Templates to File. The format * is an efficient binary format. File must already be open * for writing. * @param File open file to write templates to * @param Templates templates to save into File * @param target_unicharset the UNICHARSET to use * @return none * @note Globals: none * @note Exceptions: none * @note History: Wed Feb 27 11:48:46 1991, DSJ, Created. */ void Classify::WriteIntTemplates(FILE *File, INT_TEMPLATES Templates, const UNICHARSET& target_unicharset) { int i, j; INT_CLASS Class; int unicharset_size = target_unicharset.size(); int version_id = -5; // When negated by the reader -1 becomes +1 etc. if (Templates->NumClasses != unicharset_size) { cprintf("Warning: executing WriteIntTemplates() with %d classes in" " Templates, while target_unicharset size is %d\n", Templates->NumClasses, unicharset_size); } /* first write the high level template struct */ fwrite(&unicharset_size, sizeof(unicharset_size), 1, File); fwrite(&version_id, sizeof(version_id), 1, File); fwrite(&Templates->NumClassPruners, sizeof(Templates->NumClassPruners), 1, File); fwrite(&Templates->NumClasses, sizeof(Templates->NumClasses), 1, File); /* then write out the class pruners */ for (i = 0; i < Templates->NumClassPruners; i++) fwrite(Templates->ClassPruners[i], sizeof(CLASS_PRUNER_STRUCT), 1, File); /* then write out each class */ for (i = 0; i < Templates->NumClasses; i++) { Class = Templates->Class[i]; /* first write out the high level struct for the class */ fwrite(&Class->NumProtos, sizeof(Class->NumProtos), 1, File); fwrite(&Class->NumProtoSets, sizeof(Class->NumProtoSets), 1, File); ASSERT_HOST(Class->NumConfigs == this->fontset_table_.get(Class->font_set_id).size); fwrite(&Class->NumConfigs, sizeof(Class->NumConfigs), 1, File); for (j = 0; j < Class->NumConfigs; ++j) { fwrite(&Class->ConfigLengths[j], sizeof(uinT16), 1, File); } /* then write out the proto lengths */ if (MaxNumIntProtosIn (Class) > 0) { fwrite ((char *) (Class->ProtoLengths), sizeof (uinT8), MaxNumIntProtosIn (Class), File); } /* then write out the proto sets */ for (j = 0; j < Class->NumProtoSets; j++) fwrite ((char *) Class->ProtoSets[j], sizeof (PROTO_SET_STRUCT), 1, File); /* then write the fonts info */ fwrite(&Class->font_set_id, sizeof(int), 1, File); } /* Write the fonts info tables */ this->fontinfo_table_.write(File, NewPermanentTessCallback(write_info)); this->fontinfo_table_.write(File, NewPermanentTessCallback(write_spacing_info)); this->fontset_table_.write(File, NewPermanentTessCallback(write_set)); } /* WriteIntTemplates */ } // namespace tesseract /*----------------------------------------------------------------------------- Private Code -----------------------------------------------------------------------------*/ /** * This routine returns the parameter value which * corresponds to the beginning of the specified bucket. * The bucket number should have been generated using the * BucketFor() function with parameters Offset and NumBuckets. * @param Bucket bucket whose start is to be computed * @param Offset offset used to map params to buckets * @param NumBuckets total number of buckets * @return Param value corresponding to start position of Bucket. * @note Globals: none * @note Exceptions: none * @note History: Thu Feb 14 13:24:33 1991, DSJ, Created. */ FLOAT32 BucketStart(int Bucket, FLOAT32 Offset, int NumBuckets) { return (((FLOAT32) Bucket / NumBuckets) - Offset); } /* BucketStart */ /** * This routine returns the parameter value which * corresponds to the end of the specified bucket. * The bucket number should have been generated using the * BucketFor() function with parameters Offset and NumBuckets. * @param Bucket bucket whose end is to be computed * @param Offset offset used to map params to buckets * @param NumBuckets total number of buckets * @return Param value corresponding to end position of Bucket. * @note Globals: none * @note Exceptions: none * @note History: Thu Feb 14 13:24:33 1991, DSJ, Created. */ FLOAT32 BucketEnd(int Bucket, FLOAT32 Offset, int NumBuckets) { return (((FLOAT32) (Bucket + 1) / NumBuckets) - Offset); } /* BucketEnd */ /** * This routine fills in the section of a class pruner * corresponding to a single x value for a single proto of * a class. * @param FillSpec specifies which bits to fill in pruner * @param Pruner class pruner to be filled * @param ClassMask indicates which bits to change in each word * @param ClassCount indicates what to change bits to * @param WordIndex indicates which word to change * @return none * @note Globals: none * @note Exceptions: none * @note History: Tue Feb 19 11:11:29 1991, DSJ, Created. */ void DoFill(FILL_SPEC *FillSpec, CLASS_PRUNER_STRUCT* Pruner, register uinT32 ClassMask, register uinT32 ClassCount, register uinT32 WordIndex) { int X, Y, Angle; uinT32 OldWord; X = FillSpec->X; if (X < 0) X = 0; if (X >= NUM_CP_BUCKETS) X = NUM_CP_BUCKETS - 1; if (FillSpec->YStart < 0) FillSpec->YStart = 0; if (FillSpec->YEnd >= NUM_CP_BUCKETS) FillSpec->YEnd = NUM_CP_BUCKETS - 1; for (Y = FillSpec->YStart; Y <= FillSpec->YEnd; Y++) for (Angle = FillSpec->AngleStart; TRUE; CircularIncrement (Angle, NUM_CP_BUCKETS)) { OldWord = Pruner->p[X][Y][Angle][WordIndex]; if (ClassCount > (OldWord & ClassMask)) { OldWord &= ~ClassMask; OldWord |= ClassCount; Pruner->p[X][Y][Angle][WordIndex] = OldWord; } if (Angle == FillSpec->AngleEnd) break; } } /* DoFill */ /** * Return TRUE if the specified table filler is done, i.e. * if it has no more lines to fill. * @param Filler table filler to check if done * @return TRUE if no more lines to fill, FALSE otherwise. * @note Globals: none * @note Exceptions: none * @note History: Tue Feb 19 10:08:05 1991, DSJ, Created. */ BOOL8 FillerDone(TABLE_FILLER *Filler) { FILL_SWITCH *Next; Next = &(Filler->Switch[Filler->NextSwitch]); if (Filler->X > Next->X && Next->Type == LastSwitch) return (TRUE); else return (FALSE); } /* FillerDone */ /** * This routine sets Bit in each bit vector whose * bucket lies within the range Center +- Spread. The fill * is done for a circular dimension, i.e. bucket 0 is adjacent * to the last bucket. It is assumed that Center and Spread * are expressed in a circular coordinate system whose range * is 0 to 1. * @param ParamTable table of bit vectors, one per param bucket * @param Bit bit position in vectors to be filled * @param Center center of filled area * @param Spread spread of filled area * @param debug debug flag * @return none * @note Globals: none * @note Exceptions: none * @note History: Tue Oct 16 09:26:54 1990, DSJ, Created. */ void FillPPCircularBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug) { int i, FirstBucket, LastBucket; if (Spread > 0.5) Spread = 0.5; FirstBucket = (int) floor ((Center - Spread) * NUM_PP_BUCKETS); if (FirstBucket < 0) FirstBucket += NUM_PP_BUCKETS; LastBucket = (int) floor ((Center + Spread) * NUM_PP_BUCKETS); if (LastBucket >= NUM_PP_BUCKETS) LastBucket -= NUM_PP_BUCKETS; if (debug) tprintf("Circular fill from %d to %d", FirstBucket, LastBucket); for (i = FirstBucket; TRUE; CircularIncrement (i, NUM_PP_BUCKETS)) { SET_BIT (ParamTable[i], Bit); /* exit loop after we have set the bit for the last bucket */ if (i == LastBucket) break; } } /* FillPPCircularBits */ /** * This routine sets Bit in each bit vector whose * bucket lies within the range Center +- Spread. The fill * is done for a linear dimension, i.e. there is no wrap-around * for this dimension. It is assumed that Center and Spread * are expressed in a linear coordinate system whose range * is approximately 0 to 1. Values outside this range will * be clipped. * @param ParamTable table of bit vectors, one per param bucket * @param Bit bit number being filled * @param Center center of filled area * @param Spread spread of filled area * @param debug debug flag * @return none * @note Globals: none * @note Exceptions: none * @note History: Tue Oct 16 09:26:54 1990, DSJ, Created. */ void FillPPLinearBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug) { int i, FirstBucket, LastBucket; FirstBucket = (int) floor ((Center - Spread) * NUM_PP_BUCKETS); if (FirstBucket < 0) FirstBucket = 0; LastBucket = (int) floor ((Center + Spread) * NUM_PP_BUCKETS); if (LastBucket >= NUM_PP_BUCKETS) LastBucket = NUM_PP_BUCKETS - 1; if (debug) tprintf("Linear fill from %d to %d", FirstBucket, LastBucket); for (i = FirstBucket; i <= LastBucket; i++) SET_BIT (ParamTable[i], Bit); } /* FillPPLinearBits */ /*---------------------------------------------------------------------------*/ #ifndef GRAPHICS_DISABLED namespace tesseract { /** * This routine prompts the user with Prompt and waits * for the user to enter something in the debug window. * @param Prompt prompt to print while waiting for input from window * @param adaptive_on * @param pretrained_on * @param shape_id * @return Character entered in the debug window. * @note Globals: none * @note Exceptions: none * @note History: Thu Mar 21 16:55:13 1991, DSJ, Created. */ CLASS_ID Classify::GetClassToDebug(const char *Prompt, bool* adaptive_on, bool* pretrained_on, int* shape_id) { tprintf("%s\n", Prompt); SVEvent* ev; SVEventType ev_type; int unichar_id = INVALID_UNICHAR_ID; // Wait until a click or popup event. do { ev = IntMatchWindow->AwaitEvent(SVET_ANY); ev_type = ev->type; if (ev_type == SVET_POPUP) { if (ev->command_id == IDA_SHAPE_INDEX) { if (shape_table_ != NULL) { *shape_id = atoi(ev->parameter); *adaptive_on = false; *pretrained_on = true; if (*shape_id >= 0 && *shape_id < shape_table_->NumShapes()) { int font_id; shape_table_->GetFirstUnicharAndFont(*shape_id, &unichar_id, &font_id); tprintf("Shape %d, first unichar=%d, font=%d\n", *shape_id, unichar_id, font_id); return unichar_id; } tprintf("Shape index '%s' not found in shape table\n", ev->parameter); } else { tprintf("No shape table loaded!\n"); } } else { if (unicharset.contains_unichar(ev->parameter)) { unichar_id = unicharset.unichar_to_id(ev->parameter); if (ev->command_id == IDA_ADAPTIVE) { *adaptive_on = true; *pretrained_on = false; *shape_id = -1; } else if (ev->command_id == IDA_STATIC) { *adaptive_on = false; *pretrained_on = true; } else { *adaptive_on = true; *pretrained_on = true; } if (ev->command_id == IDA_ADAPTIVE || shape_table_ == NULL) { *shape_id = -1; return unichar_id; } for (int s = 0; s < shape_table_->NumShapes(); ++s) { if (shape_table_->GetShape(s).ContainsUnichar(unichar_id)) { tprintf("%s\n", shape_table_->DebugStr(s).string()); } } } else { tprintf("Char class '%s' not found in unicharset", ev->parameter); } } } delete ev; } while (ev_type != SVET_CLICK); return 0; } /* GetClassToDebug */ } // namespace tesseract #endif /** * This routine copies the appropriate global pad variables * into EndPad, SidePad, and AnglePad. This is a kludge used * to get around the fact that global control variables cannot * be arrays. If the specified level is illegal, the tightest * possible pads are returned. * @param Level "tightness" level to return pads for * @param EndPad place to put end pad for Level * @param SidePad place to put side pad for Level * @param AnglePad place to put angle pad for Level * @return none (results are returned in EndPad, SidePad, and AnglePad. * @note Globals: none * @note Exceptions: none * @note History: Thu Feb 14 08:26:49 1991, DSJ, Created. */ void GetCPPadsForLevel(int Level, FLOAT32 *EndPad, FLOAT32 *SidePad, FLOAT32 *AnglePad) { switch (Level) { case 0: *EndPad = classify_cp_end_pad_loose * GetPicoFeatureLength (); *SidePad = classify_cp_side_pad_loose * GetPicoFeatureLength (); *AnglePad = classify_cp_angle_pad_loose / 360.0; break; case 1: *EndPad = classify_cp_end_pad_medium * GetPicoFeatureLength (); *SidePad = classify_cp_side_pad_medium * GetPicoFeatureLength (); *AnglePad = classify_cp_angle_pad_medium / 360.0; break; case 2: *EndPad = classify_cp_end_pad_tight * GetPicoFeatureLength (); *SidePad = classify_cp_side_pad_tight * GetPicoFeatureLength (); *AnglePad = classify_cp_angle_pad_tight / 360.0; break; default: *EndPad = classify_cp_end_pad_tight * GetPicoFeatureLength (); *SidePad = classify_cp_side_pad_tight * GetPicoFeatureLength (); *AnglePad = classify_cp_angle_pad_tight / 360.0; break; } if (*AnglePad > 0.5) *AnglePad = 0.5; } /* GetCPPadsForLevel */ /** * @param Evidence evidence value to return color for * @return Color which corresponds to specified Evidence value. * @note Globals: none * @note Exceptions: none * @note History: Thu Mar 21 15:24:52 1991, DSJ, Created. */ ScrollView::Color GetMatchColorFor(FLOAT32 Evidence) { assert (Evidence >= 0.0); assert (Evidence <= 1.0); if (Evidence >= 0.90) return ScrollView::WHITE; else if (Evidence >= 0.75) return ScrollView::GREEN; else if (Evidence >= 0.50) return ScrollView::RED; else return ScrollView::BLUE; } /* GetMatchColorFor */ /** * This routine returns (in Fill) the specification of * the next line to be filled from Filler. FillerDone() should * always be called before GetNextFill() to ensure that we * do not run past the end of the fill table. * @param Filler filler to get next fill spec from * @param Fill place to put spec for next fill * @return none (results are returned in Fill) * @note Globals: none * @note Exceptions: none * @note History: Tue Feb 19 10:17:42 1991, DSJ, Created. */ void GetNextFill(TABLE_FILLER *Filler, FILL_SPEC *Fill) { FILL_SWITCH *Next; /* compute the fill assuming no switches will be encountered */ Fill->AngleStart = Filler->AngleStart; Fill->AngleEnd = Filler->AngleEnd; Fill->X = Filler->X; Fill->YStart = Filler->YStart >> 8; Fill->YEnd = Filler->YEnd >> 8; /* update the fill info and the filler for ALL switches at this X value */ Next = &(Filler->Switch[Filler->NextSwitch]); while (Filler->X >= Next->X) { Fill->X = Filler->X = Next->X; if (Next->Type == StartSwitch) { Fill->YStart = Next->Y; Filler->StartDelta = Next->Delta; Filler->YStart = Next->YInit; } else if (Next->Type == EndSwitch) { Fill->YEnd = Next->Y; Filler->EndDelta = Next->Delta; Filler->YEnd = Next->YInit; } else { /* Type must be LastSwitch */ break; } Filler->NextSwitch++; Next = &(Filler->Switch[Filler->NextSwitch]); } /* prepare the filler for the next call to this routine */ Filler->X++; Filler->YStart += Filler->StartDelta; Filler->YEnd += Filler->EndDelta; } /* GetNextFill */ /** * This routine computes a data structure (Filler) * which can be used to fill in a rectangle surrounding * the specified Proto. * * @param EndPad, SidePad, AnglePad padding to add to proto * @param Proto proto to create a filler for * @param Filler place to put table filler * * @return none (results are returned in Filler) * @note Globals: none * @note Exceptions: none * @note History: Thu Feb 14 09:27:05 1991, DSJ, Created. */ void InitTableFiller (FLOAT32 EndPad, FLOAT32 SidePad, FLOAT32 AnglePad, PROTO Proto, TABLE_FILLER * Filler) #define XS X_SHIFT #define YS Y_SHIFT #define AS ANGLE_SHIFT #define NB NUM_CP_BUCKETS { FLOAT32 Angle; FLOAT32 X, Y, HalfLength; FLOAT32 Cos, Sin; FLOAT32 XAdjust, YAdjust; FPOINT Start, Switch1, Switch2, End; int S1 = 0; int S2 = 1; Angle = Proto->Angle; X = Proto->X; Y = Proto->Y; HalfLength = Proto->Length / 2.0; Filler->AngleStart = CircBucketFor(Angle - AnglePad, AS, NB); Filler->AngleEnd = CircBucketFor(Angle + AnglePad, AS, NB); Filler->NextSwitch = 0; if (fabs (Angle - 0.0) < HV_TOLERANCE || fabs (Angle - 0.5) < HV_TOLERANCE) { /* horizontal proto - handle as special case */ Filler->X = Bucket8For(X - HalfLength - EndPad, XS, NB); Filler->YStart = Bucket16For(Y - SidePad, YS, NB * 256); Filler->YEnd = Bucket16For(Y + SidePad, YS, NB * 256); Filler->StartDelta = 0; Filler->EndDelta = 0; Filler->Switch[0].Type = LastSwitch; Filler->Switch[0].X = Bucket8For(X + HalfLength + EndPad, XS, NB); } else if (fabs(Angle - 0.25) < HV_TOLERANCE || fabs(Angle - 0.75) < HV_TOLERANCE) { /* vertical proto - handle as special case */ Filler->X = Bucket8For(X - SidePad, XS, NB); Filler->YStart = Bucket16For(Y - HalfLength - EndPad, YS, NB * 256); Filler->YEnd = Bucket16For(Y + HalfLength + EndPad, YS, NB * 256); Filler->StartDelta = 0; Filler->EndDelta = 0; Filler->Switch[0].Type = LastSwitch; Filler->Switch[0].X = Bucket8For(X + SidePad, XS, NB); } else { /* diagonal proto */ if ((Angle > 0.0 && Angle < 0.25) || (Angle > 0.5 && Angle < 0.75)) { /* rising diagonal proto */ Angle *= 2.0 * PI; Cos = fabs(cos(Angle)); Sin = fabs(sin(Angle)); /* compute the positions of the corners of the acceptance region */ Start.x = X - (HalfLength + EndPad) * Cos - SidePad * Sin; Start.y = Y - (HalfLength + EndPad) * Sin + SidePad * Cos; End.x = 2.0 * X - Start.x; End.y = 2.0 * Y - Start.y; Switch1.x = X - (HalfLength + EndPad) * Cos + SidePad * Sin; Switch1.y = Y - (HalfLength + EndPad) * Sin - SidePad * Cos; Switch2.x = 2.0 * X - Switch1.x; Switch2.y = 2.0 * Y - Switch1.y; if (Switch1.x > Switch2.x) { S1 = 1; S2 = 0; } /* translate into bucket positions and deltas */ Filler->X = Bucket8For(Start.x, XS, NB); Filler->StartDelta = -(inT16) ((Cos / Sin) * 256); Filler->EndDelta = (inT16) ((Sin / Cos) * 256); XAdjust = BucketEnd(Filler->X, XS, NB) - Start.x; YAdjust = XAdjust * Cos / Sin; Filler->YStart = Bucket16For(Start.y - YAdjust, YS, NB * 256); YAdjust = XAdjust * Sin / Cos; Filler->YEnd = Bucket16For(Start.y + YAdjust, YS, NB * 256); Filler->Switch[S1].Type = StartSwitch; Filler->Switch[S1].X = Bucket8For(Switch1.x, XS, NB); Filler->Switch[S1].Y = Bucket8For(Switch1.y, YS, NB); XAdjust = Switch1.x - BucketStart(Filler->Switch[S1].X, XS, NB); YAdjust = XAdjust * Sin / Cos; Filler->Switch[S1].YInit = Bucket16For(Switch1.y - YAdjust, YS, NB * 256); Filler->Switch[S1].Delta = Filler->EndDelta; Filler->Switch[S2].Type = EndSwitch; Filler->Switch[S2].X = Bucket8For(Switch2.x, XS, NB); Filler->Switch[S2].Y = Bucket8For(Switch2.y, YS, NB); XAdjust = Switch2.x - BucketStart(Filler->Switch[S2].X, XS, NB); YAdjust = XAdjust * Cos / Sin; Filler->Switch[S2].YInit = Bucket16For(Switch2.y + YAdjust, YS, NB * 256); Filler->Switch[S2].Delta = Filler->StartDelta; Filler->Switch[2].Type = LastSwitch; Filler->Switch[2].X = Bucket8For(End.x, XS, NB); } else { /* falling diagonal proto */ Angle *= 2.0 * PI; Cos = fabs(cos(Angle)); Sin = fabs(sin(Angle)); /* compute the positions of the corners of the acceptance region */ Start.x = X - (HalfLength + EndPad) * Cos - SidePad * Sin; Start.y = Y + (HalfLength + EndPad) * Sin - SidePad * Cos; End.x = 2.0 * X - Start.x; End.y = 2.0 * Y - Start.y; Switch1.x = X - (HalfLength + EndPad) * Cos + SidePad * Sin; Switch1.y = Y + (HalfLength + EndPad) * Sin + SidePad * Cos; Switch2.x = 2.0 * X - Switch1.x; Switch2.y = 2.0 * Y - Switch1.y; if (Switch1.x > Switch2.x) { S1 = 1; S2 = 0; } /* translate into bucket positions and deltas */ Filler->X = Bucket8For(Start.x, XS, NB); Filler->StartDelta = -(inT16) ((Sin / Cos) * 256); Filler->EndDelta = (inT16) ((Cos / Sin) * 256); XAdjust = BucketEnd(Filler->X, XS, NB) - Start.x; YAdjust = XAdjust * Sin / Cos; Filler->YStart = Bucket16For(Start.y - YAdjust, YS, NB * 256); YAdjust = XAdjust * Cos / Sin; Filler->YEnd = Bucket16For(Start.y + YAdjust, YS, NB * 256); Filler->Switch[S1].Type = EndSwitch; Filler->Switch[S1].X = Bucket8For(Switch1.x, XS, NB); Filler->Switch[S1].Y = Bucket8For(Switch1.y, YS, NB); XAdjust = Switch1.x - BucketStart(Filler->Switch[S1].X, XS, NB); YAdjust = XAdjust * Sin / Cos; Filler->Switch[S1].YInit = Bucket16For(Switch1.y + YAdjust, YS, NB * 256); Filler->Switch[S1].Delta = Filler->StartDelta; Filler->Switch[S2].Type = StartSwitch; Filler->Switch[S2].X = Bucket8For(Switch2.x, XS, NB); Filler->Switch[S2].Y = Bucket8For(Switch2.y, YS, NB); XAdjust = Switch2.x - BucketStart(Filler->Switch[S2].X, XS, NB); YAdjust = XAdjust * Cos / Sin; Filler->Switch[S2].YInit = Bucket16For(Switch2.y - YAdjust, YS, NB * 256); Filler->Switch[S2].Delta = Filler->EndDelta; Filler->Switch[2].Type = LastSwitch; Filler->Switch[2].X = Bucket8For(End.x, XS, NB); } } } /* InitTableFiller */ /*---------------------------------------------------------------------------*/ #ifndef GRAPHICS_DISABLED /** * This routine renders the specified feature into ShapeList. * @param window to add feature rendering to * @param Feature feature to be rendered * @param color color to use for feature rendering * @return New shape list with rendering of Feature added. * @note Globals: none * @note Exceptions: none * @note History: Thu Mar 21 14:57:41 1991, DSJ, Created. */ void RenderIntFeature(ScrollView *window, const INT_FEATURE_STRUCT* Feature, ScrollView::Color color) { FLOAT32 X, Y, Dx, Dy, Length; window->Pen(color); assert(Feature != NULL); assert(color != 0); X = Feature->X; Y = Feature->Y; Length = GetPicoFeatureLength() * 0.7 * INT_CHAR_NORM_RANGE; // The -PI has no significant effect here, but the value of Theta is computed // using BinaryAnglePlusPi in intfx.cpp. Dx = (Length / 2.0) * cos((Feature->Theta / 256.0) * 2.0 * PI - PI); Dy = (Length / 2.0) * sin((Feature->Theta / 256.0) * 2.0 * PI - PI); window->SetCursor(X, Y); window->DrawTo(X + Dx, Y + Dy); } /* RenderIntFeature */ /** * This routine extracts the parameters of the specified * proto from the class description and adds a rendering of * the proto onto the ShapeList. * * @param window ScrollView instance * @param Class class that proto is contained in * @param ProtoId id of proto to be rendered * @param color color to render proto in * * Globals: none * * @return New shape list with a rendering of one proto added. * @note Exceptions: none * @note History: Thu Mar 21 10:21:09 1991, DSJ, Created. */ void RenderIntProto(ScrollView *window, INT_CLASS Class, PROTO_ID ProtoId, ScrollView::Color color) { PROTO_SET ProtoSet; INT_PROTO Proto; int ProtoSetIndex; int ProtoWordIndex; FLOAT32 Length; int Xmin, Xmax, Ymin, Ymax; FLOAT32 X, Y, Dx, Dy; uinT32 ProtoMask; int Bucket; assert(ProtoId >= 0); assert(Class != NULL); assert(ProtoId < Class->NumProtos); assert(color != 0); window->Pen(color); ProtoSet = Class->ProtoSets[SetForProto(ProtoId)]; ProtoSetIndex = IndexForProto(ProtoId); Proto = &(ProtoSet->Protos[ProtoSetIndex]); Length = (Class->ProtoLengths[ProtoId] * GetPicoFeatureLength() * INT_CHAR_NORM_RANGE); ProtoMask = PPrunerMaskFor(ProtoId); ProtoWordIndex = PPrunerWordIndexFor(ProtoId); // find the x and y extent of the proto from the proto pruning table Xmin = Ymin = NUM_PP_BUCKETS; Xmax = Ymax = 0; for (Bucket = 0; Bucket < NUM_PP_BUCKETS; Bucket++) { if (ProtoMask & ProtoSet->ProtoPruner[PRUNER_X][Bucket][ProtoWordIndex]) { UpdateRange(Bucket, &Xmin, &Xmax); } if (ProtoMask & ProtoSet->ProtoPruner[PRUNER_Y][Bucket][ProtoWordIndex]) { UpdateRange(Bucket, &Ymin, &Ymax); } } X = (Xmin + Xmax + 1) / 2.0 * PROTO_PRUNER_SCALE; Y = (Ymin + Ymax + 1) / 2.0 * PROTO_PRUNER_SCALE; // The -PI has no significant effect here, but the value of Theta is computed // using BinaryAnglePlusPi in intfx.cpp. Dx = (Length / 2.0) * cos((Proto->Angle / 256.0) * 2.0 * PI - PI); Dy = (Length / 2.0) * sin((Proto->Angle / 256.0) * 2.0 * PI - PI); window->SetCursor(X - Dx, Y - Dy); window->DrawTo(X + Dx, Y + Dy); } /* RenderIntProto */ #endif /** * This routine truncates Param to lie within the range * of Min-Max inclusive. If a truncation is performed, and * Id is not null, an warning message is printed. * * @param Param parameter value to be truncated * @param Min, Max parameter limits (inclusive) * @param Id string id of parameter for error messages * * Globals: none * * @return Truncated parameter. * @note Exceptions: none * @note History: Fri Feb 8 11:54:28 1991, DSJ, Created. */ int TruncateParam(FLOAT32 Param, int Min, int Max, char *Id) { if (Param < Min) { if (Id) cprintf("Warning: Param %s truncated from %f to %d!\n", Id, Param, Min); Param = Min; } else if (Param > Max) { if (Id) cprintf("Warning: Param %s truncated from %f to %d!\n", Id, Param, Max); Param = Max; } return static_cast(floor(Param)); } /* TruncateParam */ #ifndef GRAPHICS_DISABLED /** * Initializes the int matcher window if it is not already * initialized. */ void InitIntMatchWindowIfReqd() { if (IntMatchWindow == NULL) { IntMatchWindow = CreateFeatureSpaceWindow("IntMatchWindow", 50, 200); SVMenuNode* popup_menu = new SVMenuNode(); popup_menu->AddChild("Debug Adapted classes", IDA_ADAPTIVE, "x", "Class to debug"); popup_menu->AddChild("Debug Static classes", IDA_STATIC, "x", "Class to debug"); popup_menu->AddChild("Debug Both", IDA_BOTH, "x", "Class to debug"); popup_menu->AddChild("Debug Shape Index", IDA_SHAPE_INDEX, "0", "Index to debug"); popup_menu->BuildMenu(IntMatchWindow, false); } } /** * Initializes the proto display window if it is not already * initialized. */ void InitProtoDisplayWindowIfReqd() { if (ProtoDisplayWindow == NULL) { ProtoDisplayWindow = CreateFeatureSpaceWindow("ProtoDisplayWindow", 550, 200); } } /** * Initializes the feature display window if it is not already * initialized. */ void InitFeatureDisplayWindowIfReqd() { if (FeatureDisplayWindow == NULL) { FeatureDisplayWindow = CreateFeatureSpaceWindow("FeatureDisplayWindow", 50, 700); } } /// Creates a window of the appropriate size for displaying elements /// in feature space. ScrollView* CreateFeatureSpaceWindow(const char* name, int xpos, int ypos) { return new ScrollView(name, xpos, ypos, 520, 520, 260, 260, true); } #endif // GRAPHICS_DISABLED tesseract-3.04.01/classify/intproto.h000066400000000000000000000212151266071204500175240ustar00rootroot00000000000000/****************************************************************************** ** Filename: intproto.h ** Purpose: Definition of data structures for integer protos. ** Author: Dan Johnson ** History: Thu Feb 7 12:58:45 1991, 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 INTPROTO_H #define INTPROTO_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "genericvector.h" #include "matchdefs.h" #include "mfoutline.h" #include "protos.h" #include "scrollview.h" #include "unicharset.h" class FCOORD; /* define order of params in pruners */ #define PRUNER_X 0 #define PRUNER_Y 1 #define PRUNER_ANGLE 2 /* definition of coordinate system offsets for each table parameter */ #define ANGLE_SHIFT (0.0) #define X_SHIFT (0.5) #define Y_SHIFT (0.5) #define MAX_PROTO_INDEX 24 #define BITS_PER_WERD static_cast(8 * sizeof(uinT32)) /* Script detection: increase this number to 128 */ #define MAX_NUM_CONFIGS 64 #define MAX_NUM_PROTOS 512 #define PROTOS_PER_PROTO_SET 64 #define MAX_NUM_PROTO_SETS (MAX_NUM_PROTOS / PROTOS_PER_PROTO_SET) #define NUM_PP_PARAMS 3 #define NUM_PP_BUCKETS 64 #define NUM_CP_BUCKETS 24 #define CLASSES_PER_CP 32 #define NUM_BITS_PER_CLASS 2 #define CLASS_PRUNER_CLASS_MASK (~(~0 << NUM_BITS_PER_CLASS)) #define CLASSES_PER_CP_WERD (CLASSES_PER_CP / NUM_BITS_PER_CLASS) #define PROTOS_PER_PP_WERD BITS_PER_WERD #define BITS_PER_CP_VECTOR (CLASSES_PER_CP * NUM_BITS_PER_CLASS) #define MAX_NUM_CLASS_PRUNERS ((MAX_NUM_CLASSES + CLASSES_PER_CP - 1) / \ CLASSES_PER_CP) #define WERDS_PER_CP_VECTOR (BITS_PER_CP_VECTOR / BITS_PER_WERD) #define WERDS_PER_PP_VECTOR ((PROTOS_PER_PROTO_SET+BITS_PER_WERD-1)/ \ BITS_PER_WERD) #define WERDS_PER_PP (NUM_PP_PARAMS * NUM_PP_BUCKETS * \ WERDS_PER_PP_VECTOR) #define WERDS_PER_CP (NUM_CP_BUCKETS * NUM_CP_BUCKETS * \ NUM_CP_BUCKETS * WERDS_PER_CP_VECTOR) #define WERDS_PER_CONFIG_VEC ((MAX_NUM_CONFIGS + BITS_PER_WERD - 1) / \ BITS_PER_WERD) /* The first 3 dimensions of the CLASS_PRUNER_STRUCT are the * 3 axes of the quantized feature space. * The position of the the bits recorded for each class in the * 4th dimension is determined by using CPrunerWordIndexFor(c), * where c is the corresponding class id. */ struct CLASS_PRUNER_STRUCT { uinT32 p[NUM_CP_BUCKETS][NUM_CP_BUCKETS][NUM_CP_BUCKETS][WERDS_PER_CP_VECTOR]; }; typedef struct { inT8 A; uinT8 B; inT8 C; uinT8 Angle; uinT32 Configs[WERDS_PER_CONFIG_VEC]; } INT_PROTO_STRUCT, *INT_PROTO; typedef uinT32 PROTO_PRUNER[NUM_PP_PARAMS][NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR]; typedef struct { PROTO_PRUNER ProtoPruner; INT_PROTO_STRUCT Protos[PROTOS_PER_PROTO_SET]; } PROTO_SET_STRUCT, *PROTO_SET; typedef uinT32 CONFIG_PRUNER[NUM_PP_PARAMS][NUM_PP_BUCKETS][4]; typedef struct { uinT16 NumProtos; uinT8 NumProtoSets; uinT8 NumConfigs; PROTO_SET ProtoSets[MAX_NUM_PROTO_SETS]; uinT8 *ProtoLengths; uinT16 ConfigLengths[MAX_NUM_CONFIGS]; int font_set_id; // FontSet id, see above } INT_CLASS_STRUCT, *INT_CLASS; typedef struct { int NumClasses; int NumClassPruners; INT_CLASS Class[MAX_NUM_CLASSES]; CLASS_PRUNER_STRUCT* ClassPruners[MAX_NUM_CLASS_PRUNERS]; } INT_TEMPLATES_STRUCT, *INT_TEMPLATES; /* definitions of integer features*/ #define MAX_NUM_INT_FEATURES 512 #define INT_CHAR_NORM_RANGE 256 struct INT_FEATURE_STRUCT { INT_FEATURE_STRUCT() : X(0), Y(0), Theta(0), CP_misses(0) { } // Builds a feature from an FCOORD for position with all the necessary // clipping and rounding. INT_FEATURE_STRUCT(const FCOORD& pos, uinT8 theta); // Builds a feature from ints with all the necessary clipping and casting. INT_FEATURE_STRUCT(int x, int y, int theta); uinT8 X; uinT8 Y; uinT8 Theta; inT8 CP_misses; void print() const { tprintf("(%d,%d):%d\n", X, Y, Theta); } }; typedef INT_FEATURE_STRUCT *INT_FEATURE; typedef INT_FEATURE_STRUCT INT_FEATURE_ARRAY[MAX_NUM_INT_FEATURES]; enum IntmatcherDebugAction { IDA_ADAPTIVE, IDA_STATIC, IDA_SHAPE_INDEX, IDA_BOTH }; /**---------------------------------------------------------------------------- Macros ----------------------------------------------------------------------------**/ #define MaxNumIntProtosIn(C) (C->NumProtoSets * PROTOS_PER_PROTO_SET) #define SetForProto(P) (P / PROTOS_PER_PROTO_SET) #define IndexForProto(P) (P % PROTOS_PER_PROTO_SET) #define ProtoForProtoId(C,P) (&((C->ProtoSets[SetForProto (P)])-> \ Protos [IndexForProto (P)])) #define PPrunerWordIndexFor(I) (((I) % PROTOS_PER_PROTO_SET) / \ PROTOS_PER_PP_WERD) #define PPrunerBitIndexFor(I) ((I) % PROTOS_PER_PP_WERD) #define PPrunerMaskFor(I) (1 << PPrunerBitIndexFor (I)) #define MaxNumClassesIn(T) (T->NumClassPruners * CLASSES_PER_CP) #define LegalClassId(c) ((c) >= 0 && (c) <= MAX_CLASS_ID) #define UnusedClassIdIn(T,c) ((T)->Class[c] == NULL) #define ClassForClassId(T,c) ((T)->Class[c]) #define ClassPrunersFor(T) ((T)->ClassPruner) #define CPrunerIdFor(c) ((c) / CLASSES_PER_CP) #define CPrunerFor(T,c) ((T)->ClassPruners[CPrunerIdFor(c)]) #define CPrunerWordIndexFor(c) (((c) % CLASSES_PER_CP) / CLASSES_PER_CP_WERD) #define CPrunerBitIndexFor(c) (((c) % CLASSES_PER_CP) % CLASSES_PER_CP_WERD) #define CPrunerMaskFor(L,c) (((L)+1) << CPrunerBitIndexFor (c) * NUM_BITS_PER_CLASS) /* DEBUG macros*/ #define PRINT_MATCH_SUMMARY 0x001 #define DISPLAY_FEATURE_MATCHES 0x002 #define DISPLAY_PROTO_MATCHES 0x004 #define PRINT_FEATURE_MATCHES 0x008 #define PRINT_PROTO_MATCHES 0x010 #define CLIP_MATCH_EVIDENCE 0x020 #define MatchDebuggingOn(D) (D) #define PrintMatchSummaryOn(D) ((D) & PRINT_MATCH_SUMMARY) #define DisplayFeatureMatchesOn(D) ((D) & DISPLAY_FEATURE_MATCHES) #define DisplayProtoMatchesOn(D) ((D) & DISPLAY_PROTO_MATCHES) #define PrintFeatureMatchesOn(D) ((D) & PRINT_FEATURE_MATCHES) #define PrintProtoMatchesOn(D) ((D) & PRINT_PROTO_MATCHES) #define ClipMatchEvidenceOn(D) ((D) & CLIP_MATCH_EVIDENCE) /**---------------------------------------------------------------------------- Public Function Prototypes ----------------------------------------------------------------------------**/ void AddIntClass(INT_TEMPLATES Templates, CLASS_ID ClassId, INT_CLASS Class); int AddIntConfig(INT_CLASS Class); int AddIntProto(INT_CLASS Class); void AddProtoToClassPruner(PROTO Proto, CLASS_ID ClassId, INT_TEMPLATES Templates); void AddProtoToProtoPruner(PROTO Proto, int ProtoId, INT_CLASS Class, bool debug); uinT8 Bucket8For(FLOAT32 param, FLOAT32 offset, int num_buckets); uinT16 Bucket16For(FLOAT32 param, FLOAT32 offset, int num_buckets); uinT8 CircBucketFor(FLOAT32 param, FLOAT32 offset, int num_buckets); void UpdateMatchDisplay(); void ConvertConfig(BIT_VECTOR Config, int ConfigId, INT_CLASS Class); void DisplayIntFeature(const INT_FEATURE_STRUCT* Feature, FLOAT32 Evidence); void DisplayIntProto(INT_CLASS Class, PROTO_ID ProtoId, FLOAT32 Evidence); INT_CLASS NewIntClass(int MaxNumProtos, int MaxNumConfigs); INT_TEMPLATES NewIntTemplates(); void free_int_templates(INT_TEMPLATES templates); void ShowMatchDisplay(); namespace tesseract { // Clears the given window and draws the featurespace guides for the // appropriate normalization method. void ClearFeatureSpaceWindow(NORM_METHOD norm_method, ScrollView* window); } // namespace tesseract. /*----------------------------------------------------------------------------*/ #ifndef GRAPHICS_DISABLED void RenderIntFeature(ScrollView *window, const INT_FEATURE_STRUCT* Feature, ScrollView::Color color); void InitIntMatchWindowIfReqd(); void InitProtoDisplayWindowIfReqd(); void InitFeatureDisplayWindowIfReqd(); // Creates a window of the appropriate size for displaying elements // in feature space. ScrollView* CreateFeatureSpaceWindow(const char* name, int xpos, int ypos); #endif // GRAPHICS_DISABLED #endif tesseract-3.04.01/classify/kdtree.cpp000066400000000000000000000447451266071204500174740ustar00rootroot00000000000000/****************************************************************************** ** Filename: kdtree.cpp ** Purpose: Routines for managing K-D search trees ** Author: Dan Johnson ** History: 3/10/89, DSJ, Created. ** 5/23/89, DSJ, Added circular feature capability. ** 7/13/89, DSJ, Made tree nodes invisible to outside. ** ** (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 "kdtree.h" #include "const.h" #include "emalloc.h" #include "freelist.h" #include #include #define Magnitude(X) ((X) < 0 ? -(X) : (X)) #define NodeFound(N,K,D) (( (N)->Key == (K) ) && ( (N)->Data == (D) )) /*----------------------------------------------------------------------------- Global Data Definitions and Declarations -----------------------------------------------------------------------------*/ #define MINSEARCH -MAX_FLOAT32 #define MAXSEARCH MAX_FLOAT32 // Helper function to find the next essential dimension in a cycle. static int NextLevel(KDTREE *tree, int level) { do { ++level; if (level >= tree->KeySize) level = 0; } while (tree->KeyDesc[level].NonEssential); return level; } //----------------------------------------------------------------------------- /** Store the k smallest-keyed key-value pairs. */ template class MinK { public: MinK(Key max_key, int k); ~MinK(); struct Element { Element() {} Element(const Key& k, const Value& v) : key(k), value(v) {} Key key; Value value; }; bool insert(Key k, Value v); const Key& max_insertable_key(); int elements_count() { return elements_count_; } const Element* elements() { return elements_; } private: const Key max_key_; //< the maximum possible Key Element* elements_; //< unsorted array of elements int elements_count_; //< the number of results collected so far int k_; //< the number of results we want from the search int max_index_; //< the index of the result with the largest key }; template MinK::MinK(Key max_key, int k) : max_key_(max_key), elements_count_(0), k_(k < 1 ? 1 : k), max_index_(0) { elements_ = new Element[k_]; } template MinK::~MinK() { delete []elements_; } template const Key& MinK::max_insertable_key() { if (elements_count_ < k_) return max_key_; return elements_[max_index_].key; } template bool MinK::insert(Key key, Value value) { if (elements_count_ < k_) { elements_[elements_count_++] = Element(key, value); if (key > elements_[max_index_].key) max_index_ = elements_count_ - 1; return true; } else if (key < elements_[max_index_].key) { // evict the largest element. elements_[max_index_] = Element(key, value); // recompute max_index_ for (int i = 0; i < elements_count_; i++) { if (elements_[i].key > elements_[max_index_].key) max_index_ = i; } return true; } return false; } //----------------------------------------------------------------------------- /** Helper class for searching for the k closest points to query_point in tree. */ class KDTreeSearch { public: KDTreeSearch(KDTREE* tree, FLOAT32 *query_point, int k_closest); ~KDTreeSearch(); /** Return the k nearest points' data. */ void Search(int *result_count, FLOAT32 *distances, void **results); private: void SearchRec(int Level, KDNODE *SubTree); bool BoxIntersectsSearch(FLOAT32 *lower, FLOAT32 *upper); KDTREE *tree_; FLOAT32 *query_point_; MinK* results_; FLOAT32 *sb_min_; //< search box minimum FLOAT32 *sb_max_; //< search box maximum }; KDTreeSearch::KDTreeSearch(KDTREE* tree, FLOAT32 *query_point, int k_closest) : tree_(tree), query_point_(query_point) { results_ = new MinK(MAXSEARCH, k_closest); sb_min_ = new FLOAT32[tree->KeySize]; sb_max_ = new FLOAT32[tree->KeySize]; } KDTreeSearch::~KDTreeSearch() { delete results_; delete[] sb_min_; delete[] sb_max_; } /// Locate the k_closest points to query_point_, and return their distances and /// data into the given buffers. void KDTreeSearch::Search(int *result_count, FLOAT32 *distances, void **results) { if (tree_->Root.Left == NULL) { *result_count = 0; } else { for (int i = 0; i < tree_->KeySize; i++) { sb_min_[i] = tree_->KeyDesc[i].Min; sb_max_[i] = tree_->KeyDesc[i].Max; } SearchRec(0, tree_->Root.Left); int count = results_->elements_count(); *result_count = count; for (int j = 0; j < count; j++) { distances[j] = (FLOAT32) sqrt((FLOAT64)results_->elements()[j].key); results[j] = results_->elements()[j].value; } } } /*----------------------------------------------------------------------------- Public Code -----------------------------------------------------------------------------*/ /// @return a new KDTREE based on the specified parameters. /// @param KeySize # of dimensions in the K-D tree /// @param KeyDesc array of params to describe key dimensions KDTREE *MakeKDTree(inT16 KeySize, const PARAM_DESC KeyDesc[]) { KDTREE *KDTree = (KDTREE *) Emalloc( sizeof(KDTREE) + (KeySize - 1) * sizeof(PARAM_DESC)); for (int i = 0; i < KeySize; i++) { KDTree->KeyDesc[i].NonEssential = KeyDesc[i].NonEssential; KDTree->KeyDesc[i].Circular = KeyDesc[i].Circular; if (KeyDesc[i].Circular) { KDTree->KeyDesc[i].Min = KeyDesc[i].Min; KDTree->KeyDesc[i].Max = KeyDesc[i].Max; KDTree->KeyDesc[i].Range = KeyDesc[i].Max - KeyDesc[i].Min; KDTree->KeyDesc[i].HalfRange = KDTree->KeyDesc[i].Range / 2; KDTree->KeyDesc[i].MidRange = (KeyDesc[i].Max + KeyDesc[i].Min) / 2; } else { KDTree->KeyDesc[i].Min = MINSEARCH; KDTree->KeyDesc[i].Max = MAXSEARCH; } } KDTree->KeySize = KeySize; KDTree->Root.Left = NULL; KDTree->Root.Right = NULL; return KDTree; } /** * This routine stores Data in the K-D tree specified by Tree * using Key as an access key. * * @param Tree K-D tree in which data is to be stored * @param Key ptr to key by which data can be retrieved * @param Data ptr to data to be stored in the tree * * @note Exceptions: none * @note History: 3/10/89, DSJ, Created. * 7/13/89, DSJ, Changed return to void. */ void KDStore(KDTREE *Tree, FLOAT32 *Key, void *Data) { int Level; KDNODE *Node; KDNODE **PtrToNode; PtrToNode = &(Tree->Root.Left); Node = *PtrToNode; Level = NextLevel(Tree, -1); while (Node != NULL) { if (Key[Level] < Node->BranchPoint) { PtrToNode = &(Node->Left); if (Key[Level] > Node->LeftBranch) Node->LeftBranch = Key[Level]; } else { PtrToNode = &(Node->Right); if (Key[Level] < Node->RightBranch) Node->RightBranch = Key[Level]; } Level = NextLevel(Tree, Level); Node = *PtrToNode; } *PtrToNode = MakeKDNode(Tree, Key, (void *) Data, Level); } /* KDStore */ /** * This routine deletes a node from Tree. The node to be * deleted is specified by the Key for the node and the Data * contents of the node. These two pointers must be identical * to the pointers that were used for the node when it was * originally stored in the tree. A node will be deleted from * the tree only if its key and data pointers are identical * to Key and Data respectively. The tree is re-formed by removing * the affected subtree and inserting all elements but the root. * * @param Tree K-D tree to delete node from * @param Key key of node to be deleted * @param Data data contents of node to be deleted * * @note Exceptions: none * * @note History: 3/13/89, DSJ, Created. * 7/13/89, DSJ, Specify node indirectly by key and data. */ void KDDelete (KDTREE * Tree, FLOAT32 Key[], void *Data) { int Level; KDNODE *Current; KDNODE *Father; /* initialize search at root of tree */ Father = &(Tree->Root); Current = Father->Left; Level = NextLevel(Tree, -1); /* search tree for node to be deleted */ while ((Current != NULL) && (!NodeFound (Current, Key, Data))) { Father = Current; if (Key[Level] < Current->BranchPoint) Current = Current->Left; else Current = Current->Right; Level = NextLevel(Tree, Level); } if (Current != NULL) { /* if node to be deleted was found */ if (Current == Father->Left) { Father->Left = NULL; Father->LeftBranch = Tree->KeyDesc[Level].Min; } else { Father->Right = NULL; Father->RightBranch = Tree->KeyDesc[Level].Max; } InsertNodes(Tree, Current->Left); InsertNodes(Tree, Current->Right); FreeSubTree(Current); } } /* KDDelete */ /** * This routine searches the K-D tree specified by Tree and * finds the QuerySize nearest neighbors of Query. All neighbors * must be within MaxDistance of Query. The data contents of * the nearest neighbors * are placed in NBuffer and their distances from Query are * placed in DBuffer. * @param Tree ptr to K-D tree to be searched * @param Query ptr to query key (point in D-space) * @param QuerySize number of nearest neighbors to be found * @param MaxDistance all neighbors must be within this distance * @param NBuffer ptr to QuerySize buffer to hold nearest neighbors * @param DBuffer ptr to QuerySize buffer to hold distances * from nearest neighbor to query point * @param NumberOfResults [out] Number of nearest neighbors actually found * @note Exceptions: none * @note History: * - 3/10/89, DSJ, Created. * - 7/13/89, DSJ, Return contents of node instead of node itself. */ void KDNearestNeighborSearch( KDTREE *Tree, FLOAT32 Query[], int QuerySize, FLOAT32 MaxDistance, int *NumberOfResults, void **NBuffer, FLOAT32 DBuffer[]) { KDTreeSearch search(Tree, Query, QuerySize); search.Search(NumberOfResults, DBuffer, NBuffer); } /*---------------------------------------------------------------------------*/ /** Walk a given Tree with action. */ void KDWalk(KDTREE *Tree, void_proc action, void *context) { if (Tree->Root.Left != NULL) Walk(Tree, action, context, Tree->Root.Left, NextLevel(Tree, -1)); } /*---------------------------------------------------------------------------*/ /** * This routine frees all memory which is allocated to the * specified KD-tree. This includes the data structure for * the kd-tree itself plus the data structures for each node * in the tree. It does not include the Key and Data items * which are pointed to by the nodes. This memory is left * untouched. * @param Tree tree data structure to be released * @return none * @note Exceptions: none * @note History: 5/26/89, DSJ, Created. */ void FreeKDTree(KDTREE *Tree) { FreeSubTree(Tree->Root.Left); memfree(Tree); } /* FreeKDTree */ /*----------------------------------------------------------------------------- Private Code -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /** * This routine allocates memory for a new K-D tree node * and places the specified Key and Data into it. The * left and right subtree pointers for the node are * initialized to empty subtrees. * @param tree The tree to create the node for * @param Key Access key for new node in KD tree * @param Data ptr to data to be stored in new node * @param Index index of Key to branch on * @return pointer to new K-D tree node * @note Exceptions: None * @note History: 3/11/89, DSJ, Created. */ KDNODE *MakeKDNode(KDTREE *tree, FLOAT32 Key[], void *Data, int Index) { KDNODE *NewNode; NewNode = (KDNODE *) Emalloc (sizeof (KDNODE)); NewNode->Key = Key; NewNode->Data = Data; NewNode->BranchPoint = Key[Index]; NewNode->LeftBranch = tree->KeyDesc[Index].Min; NewNode->RightBranch = tree->KeyDesc[Index].Max; NewNode->Left = NULL; NewNode->Right = NULL; return NewNode; } /* MakeKDNode */ /*---------------------------------------------------------------------------*/ void FreeKDNode(KDNODE *Node) { memfree ((char *)Node); } /*---------------------------------------------------------------------------*/ /** * Recursively accumulate the k_closest points to query_point_ into results_. * @param Level level in tree of sub-tree to be searched * @param SubTree sub-tree to be searched */ void KDTreeSearch::SearchRec(int level, KDNODE *sub_tree) { if (level >= tree_->KeySize) level = 0; if (!BoxIntersectsSearch(sb_min_, sb_max_)) return; results_->insert(DistanceSquared(tree_->KeySize, tree_->KeyDesc, query_point_, sub_tree->Key), sub_tree->Data); if (query_point_[level] < sub_tree->BranchPoint) { if (sub_tree->Left != NULL) { FLOAT32 tmp = sb_max_[level]; sb_max_[level] = sub_tree->LeftBranch; SearchRec(NextLevel(tree_, level), sub_tree->Left); sb_max_[level] = tmp; } if (sub_tree->Right != NULL) { FLOAT32 tmp = sb_min_[level]; sb_min_[level] = sub_tree->RightBranch; SearchRec(NextLevel(tree_, level), sub_tree->Right); sb_min_[level] = tmp; } } else { if (sub_tree->Right != NULL) { FLOAT32 tmp = sb_min_[level]; sb_min_[level] = sub_tree->RightBranch; SearchRec(NextLevel(tree_, level), sub_tree->Right); sb_min_[level] = tmp; } if (sub_tree->Left != NULL) { FLOAT32 tmp = sb_max_[level]; sb_max_[level] = sub_tree->LeftBranch; SearchRec(NextLevel(tree_, level), sub_tree->Left); sb_max_[level] = tmp; } } } /*---------------------------------------------------------------------------*/ /** *Returns the Euclidean distance squared between p1 and p2 for all essential * dimensions. * @param k keys are in k-space * @param dim dimension descriptions (essential, circular, etc) * @param p1,p2 two different points in K-D space */ FLOAT32 DistanceSquared(int k, PARAM_DESC *dim, FLOAT32 p1[], FLOAT32 p2[]) { FLOAT32 total_distance = 0; for (; k > 0; k--, p1++, p2++, dim++) { if (dim->NonEssential) continue; FLOAT32 dimension_distance = *p1 - *p2; /* if this dimension is circular - check wraparound distance */ if (dim->Circular) { dimension_distance = Magnitude(dimension_distance); FLOAT32 wrap_distance = dim->Max - dim->Min - dimension_distance; dimension_distance = MIN(dimension_distance, wrap_distance); } total_distance += dimension_distance * dimension_distance; } return total_distance; } FLOAT32 ComputeDistance(int k, PARAM_DESC *dim, FLOAT32 p1[], FLOAT32 p2[]) { return sqrt(DistanceSquared(k, dim, p1, p2)); } /*---------------------------------------------------------------------------*/ /// Return whether the query region (the smallest known circle about /// query_point_ containing results->k_ points) intersects the box specified /// between lower and upper. For circular dimensions, we also check the point /// one wrap distance away from the query. bool KDTreeSearch::BoxIntersectsSearch(FLOAT32 *lower, FLOAT32 *upper) { FLOAT32 *query = query_point_; FLOAT64 total_distance = 0.0; FLOAT64 radius_squared = results_->max_insertable_key() * results_->max_insertable_key(); PARAM_DESC *dim = tree_->KeyDesc; for (int i = tree_->KeySize; i > 0; i--, dim++, query++, lower++, upper++) { if (dim->NonEssential) continue; FLOAT32 dimension_distance; if (*query < *lower) dimension_distance = *lower - *query; else if (*query > *upper) dimension_distance = *query - *upper; else dimension_distance = 0; /* if this dimension is circular - check wraparound distance */ if (dim->Circular) { FLOAT32 wrap_distance = MAX_FLOAT32; if (*query < *lower) wrap_distance = *query + dim->Max - dim->Min - *upper; else if (*query > *upper) wrap_distance = *lower - (*query - (dim->Max - dim->Min)); dimension_distance = MIN(dimension_distance, wrap_distance); } total_distance += dimension_distance * dimension_distance; if (total_distance >= radius_squared) return FALSE; } return TRUE; } /*---------------------------------------------------------------------------*/ /** * Walk a tree, calling action once on each node. * * Operation: * This routine walks through the specified sub_tree and invokes action * action at each node as follows: * action(context, data, level) * data the data contents of the node being visited, * level is the level of the node in the tree with the root being level 0. * @param tree root of the tree being walked. * @param action action to be performed at every node * @param context action's context * @param sub_tree ptr to root of subtree to be walked * @param level current level in the tree for this node */ void Walk(KDTREE *tree, void_proc action, void *context, KDNODE *sub_tree, inT32 level) { (*action)(context, sub_tree->Data, level); if (sub_tree->Left != NULL) Walk(tree, action, context, sub_tree->Left, NextLevel(tree, level)); if (sub_tree->Right != NULL) Walk(tree, action, context, sub_tree->Right, NextLevel(tree, level)); } /** Given a subtree nodes, insert all of its elements into tree. */ void InsertNodes(KDTREE *tree, KDNODE *nodes) { if (nodes == NULL) return; KDStore(tree, nodes->Key, nodes->Data); InsertNodes(tree, nodes->Left); InsertNodes(tree, nodes->Right); } /** Free all of the nodes of a sub tree. */ void FreeSubTree(KDNODE *sub_tree) { if (sub_tree != NULL) { FreeSubTree(sub_tree->Left); FreeSubTree(sub_tree->Right); memfree(sub_tree); } } tesseract-3.04.01/classify/kdtree.h000066400000000000000000000073321266071204500171300ustar00rootroot00000000000000/****************************************************************************** ** Filename: kdtree.h ** Purpose: Definition of K-D tree access routines. ** Author: Dan Johnson ** History: 3/11/89, DSJ, Created. ** 5/23/89, DSJ, Added circular feature capability. ** ** (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 KDTREE_H #define KDTREE_H /*----------------------------------------------------------------------------- Include Files and Type Defines -----------------------------------------------------------------------------*/ #include "host.h" #include "cutil.h" #include "ocrfeatures.h" /** NOTE: All circular parameters of all keys must be in the range Min <= Param < Max where Min and Max are specified in the KeyDesc parameter passed to MakeKDTree. All KD routines assume that this is true and will not operate correctly if circular parameters outside the specified range are used. */ struct KDNODE { FLOAT32 *Key; /**< search key */ void *Data; /**< data that corresponds to key */ FLOAT32 BranchPoint; /**< needed to make deletes work efficiently */ FLOAT32 LeftBranch; /**< used to optimize search pruning */ FLOAT32 RightBranch; /**< used to optimize search pruning */ struct KDNODE *Left; /**< ptrs for KD tree structure */ struct KDNODE *Right; }; struct KDTREE { inT16 KeySize; /* number of dimensions in the tree */ KDNODE Root; /* Root.Left points to actual root node */ PARAM_DESC KeyDesc[1]; /* description of each dimension */ }; /*---------------------------------------------------------------------------- Macros -----------------------------------------------------------------------------*/ #define RootOf(T) ((T)->Root.Left->Data) /*----------------------------------------------------------------------------- Public Function Prototypes -----------------------------------------------------------------------------*/ KDTREE *MakeKDTree(inT16 KeySize, const PARAM_DESC KeyDesc[]); void KDStore(KDTREE *Tree, FLOAT32 *Key, void *Data); void KDDelete(KDTREE * Tree, FLOAT32 Key[], void *Data); void KDNearestNeighborSearch( KDTREE *Tree, FLOAT32 Query[], int QuerySize, FLOAT32 MaxDistance, int *NumberOfResults, void **NBuffer, FLOAT32 DBuffer[]); void KDWalk(KDTREE *Tree, void_proc Action, void *context); void FreeKDTree(KDTREE *Tree); /*----------------------------------------------------------------------------- Private Function Prototypes -----------------------------------------------------------------------------*/ KDNODE *MakeKDNode(KDTREE *tree, FLOAT32 Key[], void *Data, int Index); void FreeKDNode(KDNODE *Node); FLOAT32 DistanceSquared(int k, PARAM_DESC *dim, FLOAT32 p1[], FLOAT32 p2[]); FLOAT32 ComputeDistance(int k, PARAM_DESC *dim, FLOAT32 p1[], FLOAT32 p2[]); int QueryInSearch(KDTREE *tree); void Walk(KDTREE *tree, void_proc action, void *context, KDNODE *SubTree, inT32 Level); void InsertNodes(KDTREE *tree, KDNODE *nodes); void FreeSubTree(KDNODE *SubTree); #endif tesseract-3.04.01/classify/mastertrainer.cpp000066400000000000000000001165401266071204500210670ustar00rootroot00000000000000// Copyright 2010 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: mastertrainer.cpp // Description: Trainer to build the MasterClassifier. // Author: Ray Smith // Created: Wed Nov 03 18:10:01 PDT 2010 // // (C) Copyright 2010, 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 automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "mastertrainer.h" #include #include #include "allheaders.h" #include "boxread.h" #include "classify.h" #include "efio.h" #include "errorcounter.h" #include "featdefs.h" #include "sampleiterator.h" #include "shapeclassifier.h" #include "shapetable.h" #include "svmnode.h" #include "scanutils.h" namespace tesseract { // Constants controlling clustering. With a low kMinClusteredShapes and a high // kMaxUnicharsPerCluster, then kFontMergeDistance is the only limiting factor. // Min number of shapes in the output. const int kMinClusteredShapes = 1; // Max number of unichars in any individual cluster. const int kMaxUnicharsPerCluster = 2000; // Mean font distance below which to merge fonts and unichars. const float kFontMergeDistance = 0.025; MasterTrainer::MasterTrainer(NormalizationMode norm_mode, bool shape_analysis, bool replicate_samples, int debug_level) : norm_mode_(norm_mode), samples_(fontinfo_table_), junk_samples_(fontinfo_table_), verify_samples_(fontinfo_table_), charsetsize_(0), enable_shape_anaylsis_(shape_analysis), enable_replication_(replicate_samples), fragments_(NULL), prev_unichar_id_(-1), debug_level_(debug_level) { } MasterTrainer::~MasterTrainer() { delete [] fragments_; for (int p = 0; p < page_images_.size(); ++p) pixDestroy(&page_images_[p]); } // WARNING! Serialize/DeSerialize are only partial, providing // enough data to get the samples back and display them. // Writes to the given file. Returns false in case of error. bool MasterTrainer::Serialize(FILE* fp) const { if (fwrite(&norm_mode_, sizeof(norm_mode_), 1, fp) != 1) return false; if (!unicharset_.save_to_file(fp)) return false; if (!feature_space_.Serialize(fp)) return false; if (!samples_.Serialize(fp)) return false; if (!junk_samples_.Serialize(fp)) return false; if (!verify_samples_.Serialize(fp)) return false; if (!master_shapes_.Serialize(fp)) return false; if (!flat_shapes_.Serialize(fp)) return false; if (!fontinfo_table_.Serialize(fp)) return false; if (!xheights_.Serialize(fp)) return false; return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool MasterTrainer::DeSerialize(bool swap, FILE* fp) { if (fread(&norm_mode_, sizeof(norm_mode_), 1, fp) != 1) return false; if (swap) { ReverseN(&norm_mode_, sizeof(norm_mode_)); } if (!unicharset_.load_from_file(fp)) return false; charsetsize_ = unicharset_.size(); if (!feature_space_.DeSerialize(swap, fp)) return false; feature_map_.Init(feature_space_); if (!samples_.DeSerialize(swap, fp)) return false; if (!junk_samples_.DeSerialize(swap, fp)) return false; if (!verify_samples_.DeSerialize(swap, fp)) return false; if (!master_shapes_.DeSerialize(swap, fp)) return false; if (!flat_shapes_.DeSerialize(swap, fp)) return false; if (!fontinfo_table_.DeSerialize(swap, fp)) return false; if (!xheights_.DeSerialize(swap, fp)) return false; return true; } // Load an initial unicharset, or set one up if the file cannot be read. void MasterTrainer::LoadUnicharset(const char* filename) { if (!unicharset_.load_from_file(filename)) { tprintf("Failed to load unicharset from file %s\n" "Building unicharset for training from scratch...\n", filename); unicharset_.clear(); UNICHARSET initialized; // Add special characters, as they were removed by the clear, but the // default constructor puts them in. unicharset_.AppendOtherUnicharset(initialized); } charsetsize_ = unicharset_.size(); delete [] fragments_; fragments_ = new int[charsetsize_]; memset(fragments_, 0, sizeof(*fragments_) * charsetsize_); samples_.LoadUnicharset(filename); junk_samples_.LoadUnicharset(filename); verify_samples_.LoadUnicharset(filename); } // Reads the samples and their features from the given .tr format file, // adding them to the trainer with the font_id from the content of the file. // See mftraining.cpp for a description of the file format. // If verification, then these are verification samples, not training. void MasterTrainer::ReadTrainingSamples(const char* page_name, const FEATURE_DEFS_STRUCT& feature_defs, bool verification) { char buffer[2048]; int int_feature_type = ShortNameToFeatureType(feature_defs, kIntFeatureType); int micro_feature_type = ShortNameToFeatureType(feature_defs, kMicroFeatureType); int cn_feature_type = ShortNameToFeatureType(feature_defs, kCNFeatureType); int geo_feature_type = ShortNameToFeatureType(feature_defs, kGeoFeatureType); FILE* fp = Efopen(page_name, "rb"); if (fp == NULL) { tprintf("Failed to open tr file: %s\n", page_name); return; } tr_filenames_.push_back(STRING(page_name)); while (fgets(buffer, sizeof(buffer), fp) != NULL) { if (buffer[0] == '\n') continue; char* space = strchr(buffer, ' '); if (space == NULL) { tprintf("Bad format in tr file, reading fontname, unichar\n"); continue; } *space++ = '\0'; int font_id = GetFontInfoId(buffer); if (font_id < 0) font_id = 0; int page_number; STRING unichar; TBOX bounding_box; if (!ParseBoxFileStr(space, &page_number, &unichar, &bounding_box)) { tprintf("Bad format in tr file, reading box coords\n"); continue; } CHAR_DESC char_desc = ReadCharDescription(feature_defs, fp); TrainingSample* sample = new TrainingSample; sample->set_font_id(font_id); sample->set_page_num(page_number + page_images_.size()); sample->set_bounding_box(bounding_box); sample->ExtractCharDesc(int_feature_type, micro_feature_type, cn_feature_type, geo_feature_type, char_desc); AddSample(verification, unichar.string(), sample); FreeCharDescription(char_desc); } charsetsize_ = unicharset_.size(); fclose(fp); } // Adds the given single sample to the trainer, setting the classid // appropriately from the given unichar_str. void MasterTrainer::AddSample(bool verification, const char* unichar, TrainingSample* sample) { if (verification) { verify_samples_.AddSample(unichar, sample); prev_unichar_id_ = -1; } else if (unicharset_.contains_unichar(unichar)) { if (prev_unichar_id_ >= 0) fragments_[prev_unichar_id_] = -1; prev_unichar_id_ = samples_.AddSample(unichar, sample); if (flat_shapes_.FindShape(prev_unichar_id_, sample->font_id()) < 0) flat_shapes_.AddShape(prev_unichar_id_, sample->font_id()); } else { int junk_id = junk_samples_.AddSample(unichar, sample); if (prev_unichar_id_ >= 0) { CHAR_FRAGMENT* frag = CHAR_FRAGMENT::parse_from_string(unichar); if (frag != NULL && frag->is_natural()) { if (fragments_[prev_unichar_id_] == 0) fragments_[prev_unichar_id_] = junk_id; else if (fragments_[prev_unichar_id_] != junk_id) fragments_[prev_unichar_id_] = -1; } delete frag; } prev_unichar_id_ = -1; } } // Loads all pages from the given tif filename and append to page_images_. // Must be called after ReadTrainingSamples, as the current number of images // is used as an offset for page numbers in the samples. void MasterTrainer::LoadPageImages(const char* filename) { int page; Pix* pix; for (page = 0; (pix = pixReadTiff(filename, page)) != NULL; ++page) { page_images_.push_back(pix); } tprintf("Loaded %d page images from %s\n", page, filename); } // Cleans up the samples after initial load from the tr files, and prior to // saving the MasterTrainer: // Remaps fragmented chars if running shape anaylsis. // Sets up the samples appropriately for class/fontwise access. // Deletes outlier samples. void MasterTrainer::PostLoadCleanup() { if (debug_level_ > 0) tprintf("PostLoadCleanup...\n"); if (enable_shape_anaylsis_) ReplaceFragmentedSamples(); SampleIterator sample_it; sample_it.Init(NULL, NULL, true, &verify_samples_); sample_it.NormalizeSamples(); verify_samples_.OrganizeByFontAndClass(); samples_.IndexFeatures(feature_space_); // TODO(rays) DeleteOutliers is currently turned off to prove NOP-ness // against current training. // samples_.DeleteOutliers(feature_space_, debug_level_ > 0); samples_.OrganizeByFontAndClass(); if (debug_level_ > 0) tprintf("ComputeCanonicalSamples...\n"); samples_.ComputeCanonicalSamples(feature_map_, debug_level_ > 0); } // Gets the samples ready for training. Use after both // ReadTrainingSamples+PostLoadCleanup or DeSerialize. // Re-indexes the features and computes canonical and cloud features. void MasterTrainer::PreTrainingSetup() { if (debug_level_ > 0) tprintf("PreTrainingSetup...\n"); samples_.IndexFeatures(feature_space_); samples_.ComputeCanonicalFeatures(); if (debug_level_ > 0) tprintf("ComputeCloudFeatures...\n"); samples_.ComputeCloudFeatures(feature_space_.Size()); } // Sets up the master_shapes_ table, which tells which fonts should stay // together until they get to a leaf node classifier. void MasterTrainer::SetupMasterShapes() { tprintf("Building master shape table\n"); int num_fonts = samples_.NumFonts(); ShapeTable char_shapes_begin_fragment(samples_.unicharset()); ShapeTable char_shapes_end_fragment(samples_.unicharset()); ShapeTable char_shapes(samples_.unicharset()); for (int c = 0; c < samples_.charsetsize(); ++c) { ShapeTable shapes(samples_.unicharset()); for (int f = 0; f < num_fonts; ++f) { if (samples_.NumClassSamples(f, c, true) > 0) shapes.AddShape(c, f); } ClusterShapes(kMinClusteredShapes, 1, kFontMergeDistance, &shapes); const CHAR_FRAGMENT *fragment = samples_.unicharset().get_fragment(c); if (fragment == NULL) char_shapes.AppendMasterShapes(shapes, NULL); else if (fragment->is_beginning()) char_shapes_begin_fragment.AppendMasterShapes(shapes, NULL); else if (fragment->is_ending()) char_shapes_end_fragment.AppendMasterShapes(shapes, NULL); else char_shapes.AppendMasterShapes(shapes, NULL); } ClusterShapes(kMinClusteredShapes, kMaxUnicharsPerCluster, kFontMergeDistance, &char_shapes_begin_fragment); char_shapes.AppendMasterShapes(char_shapes_begin_fragment, NULL); ClusterShapes(kMinClusteredShapes, kMaxUnicharsPerCluster, kFontMergeDistance, &char_shapes_end_fragment); char_shapes.AppendMasterShapes(char_shapes_end_fragment, NULL); ClusterShapes(kMinClusteredShapes, kMaxUnicharsPerCluster, kFontMergeDistance, &char_shapes); master_shapes_.AppendMasterShapes(char_shapes, NULL); tprintf("Master shape_table:%s\n", master_shapes_.SummaryStr().string()); } // Adds the junk_samples_ to the main samples_ set. Junk samples are initially // fragments and n-grams (all incorrectly segmented characters). // Various training functions may result in incorrectly segmented characters // being added to the unicharset of the main samples, perhaps because they // form a "radical" decomposition of some (Indic) grapheme, or because they // just look the same as a real character (like rn/m) // This function moves all the junk samples, to the main samples_ set, but // desirable junk, being any sample for which the unichar already exists in // the samples_ unicharset gets the unichar-ids re-indexed to match, but // anything else gets re-marked as unichar_id 0 (space character) to identify // it as junk to the error counter. void MasterTrainer::IncludeJunk() { // Get ids of fragments in junk_samples_ that replace the dead chars. const UNICHARSET& junk_set = junk_samples_.unicharset(); const UNICHARSET& sample_set = samples_.unicharset(); int num_junks = junk_samples_.num_samples(); tprintf("Moving %d junk samples to master sample set.\n", num_junks); for (int s = 0; s < num_junks; ++s) { TrainingSample* sample = junk_samples_.mutable_sample(s); int junk_id = sample->class_id(); const char* junk_utf8 = junk_set.id_to_unichar(junk_id); int sample_id = sample_set.unichar_to_id(junk_utf8); if (sample_id == INVALID_UNICHAR_ID) sample_id = 0; sample->set_class_id(sample_id); junk_samples_.extract_sample(s); samples_.AddSample(sample_id, sample); } junk_samples_.DeleteDeadSamples(); samples_.OrganizeByFontAndClass(); } // Replicates the samples and perturbs them if the enable_replication_ flag // is set. MUST be used after the last call to OrganizeByFontAndClass on // the training samples, ie after IncludeJunk if it is going to be used, as // OrganizeByFontAndClass will eat the replicated samples into the regular // samples. void MasterTrainer::ReplicateAndRandomizeSamplesIfRequired() { if (enable_replication_) { if (debug_level_ > 0) tprintf("ReplicateAndRandomize...\n"); verify_samples_.ReplicateAndRandomizeSamples(); samples_.ReplicateAndRandomizeSamples(); samples_.IndexFeatures(feature_space_); } } // Loads the basic font properties file into fontinfo_table_. // Returns false on failure. bool MasterTrainer::LoadFontInfo(const char* filename) { FILE* fp = fopen(filename, "rb"); if (fp == NULL) { fprintf(stderr, "Failed to load font_properties from %s\n", filename); return false; } int italic, bold, fixed, serif, fraktur; while (!feof(fp)) { FontInfo fontinfo; char* font_name = new char[1024]; fontinfo.name = font_name; fontinfo.properties = 0; fontinfo.universal_id = 0; if (tfscanf(fp, "%1024s %i %i %i %i %i\n", font_name, &italic, &bold, &fixed, &serif, &fraktur) != 6) continue; fontinfo.properties = (italic << 0) + (bold << 1) + (fixed << 2) + (serif << 3) + (fraktur << 4); if (!fontinfo_table_.contains(fontinfo)) { fontinfo_table_.push_back(fontinfo); } } fclose(fp); return true; } // Loads the xheight font properties file into xheights_. // Returns false on failure. bool MasterTrainer::LoadXHeights(const char* filename) { tprintf("fontinfo table is of size %d\n", fontinfo_table_.size()); xheights_.init_to_size(fontinfo_table_.size(), -1); if (filename == NULL) return true; FILE *f = fopen(filename, "rb"); if (f == NULL) { fprintf(stderr, "Failed to load font xheights from %s\n", filename); return false; } tprintf("Reading x-heights from %s ...\n", filename); FontInfo fontinfo; fontinfo.properties = 0; // Not used to lookup in the table. fontinfo.universal_id = 0; char buffer[1024]; int xht; int total_xheight = 0; int xheight_count = 0; while (!feof(f)) { if (tfscanf(f, "%1023s %d\n", buffer, &xht) != 2) continue; buffer[1023] = '\0'; fontinfo.name = buffer; if (!fontinfo_table_.contains(fontinfo)) continue; int fontinfo_id = fontinfo_table_.get_index(fontinfo); xheights_[fontinfo_id] = xht; total_xheight += xht; ++xheight_count; } if (xheight_count == 0) { fprintf(stderr, "No valid xheights in %s!\n", filename); fclose(f); return false; } int mean_xheight = DivRounded(total_xheight, xheight_count); for (int i = 0; i < fontinfo_table_.size(); ++i) { if (xheights_[i] < 0) xheights_[i] = mean_xheight; } fclose(f); return true; } // LoadXHeights // Reads spacing stats from filename and adds them to fontinfo_table. bool MasterTrainer::AddSpacingInfo(const char *filename) { FILE* fontinfo_file = fopen(filename, "rb"); if (fontinfo_file == NULL) return true; // We silently ignore missing files! // Find the fontinfo_id. int fontinfo_id = GetBestMatchingFontInfoId(filename); if (fontinfo_id < 0) { tprintf("No font found matching fontinfo filename %s\n", filename); fclose(fontinfo_file); return false; } tprintf("Reading spacing from %s for font %d...\n", filename, fontinfo_id); // TODO(rays) scale should probably be a double, but keep as an int for now // to duplicate current behavior. int scale = kBlnXHeight / xheights_[fontinfo_id]; int num_unichars; char uch[UNICHAR_LEN]; char kerned_uch[UNICHAR_LEN]; int x_gap, x_gap_before, x_gap_after, num_kerned; ASSERT_HOST(tfscanf(fontinfo_file, "%d\n", &num_unichars) == 1); FontInfo *fi = &fontinfo_table_.get(fontinfo_id); fi->init_spacing(unicharset_.size()); FontSpacingInfo *spacing = NULL; for (int l = 0; l < num_unichars; ++l) { if (tfscanf(fontinfo_file, "%s %d %d %d", uch, &x_gap_before, &x_gap_after, &num_kerned) != 4) { tprintf("Bad format of font spacing file %s\n", filename); fclose(fontinfo_file); return false; } bool valid = unicharset_.contains_unichar(uch); if (valid) { spacing = new FontSpacingInfo(); spacing->x_gap_before = static_cast(x_gap_before * scale); spacing->x_gap_after = static_cast(x_gap_after * scale); } for (int k = 0; k < num_kerned; ++k) { if (tfscanf(fontinfo_file, "%s %d", kerned_uch, &x_gap) != 2) { tprintf("Bad format of font spacing file %s\n", filename); fclose(fontinfo_file); delete spacing; return false; } if (!valid || !unicharset_.contains_unichar(kerned_uch)) continue; spacing->kerned_unichar_ids.push_back( unicharset_.unichar_to_id(kerned_uch)); spacing->kerned_x_gaps.push_back(static_cast(x_gap * scale)); } if (valid) fi->add_spacing(unicharset_.unichar_to_id(uch), spacing); } fclose(fontinfo_file); return true; } // Returns the font id corresponding to the given font name. // Returns -1 if the font cannot be found. int MasterTrainer::GetFontInfoId(const char* font_name) { FontInfo fontinfo; // We are only borrowing the string, so it is OK to const cast it. fontinfo.name = const_cast(font_name); fontinfo.properties = 0; // Not used to lookup in the table fontinfo.universal_id = 0; return fontinfo_table_.get_index(fontinfo); } // Returns the font_id of the closest matching font name to the given // filename. It is assumed that a substring of the filename will match // one of the fonts. If more than one is matched, the longest is returned. int MasterTrainer::GetBestMatchingFontInfoId(const char* filename) { int fontinfo_id = -1; int best_len = 0; for (int f = 0; f < fontinfo_table_.size(); ++f) { if (strstr(filename, fontinfo_table_.get(f).name) != NULL) { int len = strlen(fontinfo_table_.get(f).name); // Use the longest matching length in case a substring of a font matched. if (len > best_len) { best_len = len; fontinfo_id = f; } } } return fontinfo_id; } // Sets up a flat shapetable with one shape per class/font combination. void MasterTrainer::SetupFlatShapeTable(ShapeTable* shape_table) { // To exactly mimic the results of the previous implementation, the shapes // must be clustered in order the fonts arrived, and reverse order of the // characters within each font. // Get a list of the fonts in the order they appeared. GenericVector active_fonts; int num_shapes = flat_shapes_.NumShapes(); for (int s = 0; s < num_shapes; ++s) { int font = flat_shapes_.GetShape(s)[0].font_ids[0]; int f = 0; for (f = 0; f < active_fonts.size(); ++f) { if (active_fonts[f] == font) break; } if (f == active_fonts.size()) active_fonts.push_back(font); } // For each font in order, add all the shapes with that font in reverse order. int num_fonts = active_fonts.size(); for (int f = 0; f < num_fonts; ++f) { for (int s = num_shapes - 1; s >= 0; --s) { int font = flat_shapes_.GetShape(s)[0].font_ids[0]; if (font == active_fonts[f]) { shape_table->AddShape(flat_shapes_.GetShape(s)); } } } } // Sets up a Clusterer for mftraining on a single shape_id. // Call FreeClusterer on the return value after use. CLUSTERER* MasterTrainer::SetupForClustering( const ShapeTable& shape_table, const FEATURE_DEFS_STRUCT& feature_defs, int shape_id, int* num_samples) { int desc_index = ShortNameToFeatureType(feature_defs, kMicroFeatureType); int num_params = feature_defs.FeatureDesc[desc_index]->NumParams; ASSERT_HOST(num_params == MFCount); CLUSTERER* clusterer = MakeClusterer( num_params, feature_defs.FeatureDesc[desc_index]->ParamDesc); // We want to iterate over the samples of just the one shape. IndexMapBiDi shape_map; shape_map.Init(shape_table.NumShapes(), false); shape_map.SetMap(shape_id, true); shape_map.Setup(); // Reverse the order of the samples to match the previous behavior. GenericVector sample_ptrs; SampleIterator it; it.Init(&shape_map, &shape_table, false, &samples_); for (it.Begin(); !it.AtEnd(); it.Next()) { sample_ptrs.push_back(&it.GetSample()); } int sample_id = 0; for (int i = sample_ptrs.size() - 1; i >= 0; --i) { const TrainingSample* sample = sample_ptrs[i]; int num_features = sample->num_micro_features(); for (int f = 0; f < num_features; ++f) MakeSample(clusterer, sample->micro_features()[f], sample_id); ++sample_id; } *num_samples = sample_id; return clusterer; } // Writes the given float_classes (produced by SetupForFloat2Int) as inttemp // to the given inttemp_file, and the corresponding pffmtable. // The unicharset is the original encoding of graphemes, and shape_set should // match the size of the shape_table, and may possibly be totally fake. void MasterTrainer::WriteInttempAndPFFMTable(const UNICHARSET& unicharset, const UNICHARSET& shape_set, const ShapeTable& shape_table, CLASS_STRUCT* float_classes, const char* inttemp_file, const char* pffmtable_file) { tesseract::Classify *classify = new tesseract::Classify(); // Move the fontinfo table to classify. fontinfo_table_.MoveTo(&classify->get_fontinfo_table()); INT_TEMPLATES int_templates = classify->CreateIntTemplates(float_classes, shape_set); FILE* fp = fopen(inttemp_file, "wb"); classify->WriteIntTemplates(fp, int_templates, shape_set); fclose(fp); // Now write pffmtable. This is complicated by the fact that the adaptive // classifier still wants one indexed by unichar-id, but the static // classifier needs one indexed by its shape class id. // We put the shapetable_cutoffs in a GenericVector, and compute the // unicharset cutoffs along the way. GenericVector shapetable_cutoffs; GenericVector unichar_cutoffs; for (int c = 0; c < unicharset.size(); ++c) unichar_cutoffs.push_back(0); /* then write out each class */ for (int i = 0; i < int_templates->NumClasses; ++i) { INT_CLASS Class = ClassForClassId(int_templates, i); // Todo: Test with min instead of max // int MaxLength = LengthForConfigId(Class, 0); uinT16 max_length = 0; for (int config_id = 0; config_id < Class->NumConfigs; config_id++) { // Todo: Test with min instead of max // if (LengthForConfigId (Class, config_id) < MaxLength) uinT16 length = Class->ConfigLengths[config_id]; if (length > max_length) max_length = Class->ConfigLengths[config_id]; int shape_id = float_classes[i].font_set.get(config_id); const Shape& shape = shape_table.GetShape(shape_id); for (int c = 0; c < shape.size(); ++c) { int unichar_id = shape[c].unichar_id; if (length > unichar_cutoffs[unichar_id]) unichar_cutoffs[unichar_id] = length; } } shapetable_cutoffs.push_back(max_length); } fp = fopen(pffmtable_file, "wb"); shapetable_cutoffs.Serialize(fp); for (int c = 0; c < unicharset.size(); ++c) { const char *unichar = unicharset.id_to_unichar(c); if (strcmp(unichar, " ") == 0) { unichar = "NULL"; } fprintf(fp, "%s %d\n", unichar, unichar_cutoffs[c]); } fclose(fp); free_int_templates(int_templates); delete classify; } // Generate debug output relating to the canonical distance between the // two given UTF8 grapheme strings. void MasterTrainer::DebugCanonical(const char* unichar_str1, const char* unichar_str2) { int class_id1 = unicharset_.unichar_to_id(unichar_str1); int class_id2 = unicharset_.unichar_to_id(unichar_str2); if (class_id2 == INVALID_UNICHAR_ID) class_id2 = class_id1; if (class_id1 == INVALID_UNICHAR_ID) { tprintf("No unicharset entry found for %s\n", unichar_str1); return; } else { tprintf("Font ambiguities for unichar %d = %s and %d = %s\n", class_id1, unichar_str1, class_id2, unichar_str2); } int num_fonts = samples_.NumFonts(); const IntFeatureMap& feature_map = feature_map_; // Iterate the fonts to get the similarity with other fonst of the same // class. tprintf(" "); for (int f = 0; f < num_fonts; ++f) { if (samples_.NumClassSamples(f, class_id2, false) == 0) continue; tprintf("%6d", f); } tprintf("\n"); for (int f1 = 0; f1 < num_fonts; ++f1) { // Map the features of the canonical_sample. if (samples_.NumClassSamples(f1, class_id1, false) == 0) continue; tprintf("%4d ", f1); for (int f2 = 0; f2 < num_fonts; ++f2) { if (samples_.NumClassSamples(f2, class_id2, false) == 0) continue; float dist = samples_.ClusterDistance(f1, class_id1, f2, class_id2, feature_map); tprintf(" %5.3f", dist); } tprintf("\n"); } // Build a fake ShapeTable containing all the sample types. ShapeTable shapes(unicharset_); for (int f = 0; f < num_fonts; ++f) { if (samples_.NumClassSamples(f, class_id1, true) > 0) shapes.AddShape(class_id1, f); if (class_id1 != class_id2 && samples_.NumClassSamples(f, class_id2, true) > 0) shapes.AddShape(class_id2, f); } } #ifndef GRAPHICS_DISABLED // Debugging for cloud/canonical features. // Displays a Features window containing: // If unichar_str2 is in the unicharset, and canonical_font is non-negative, // displays the canonical features of the char/font combination in red. // If unichar_str1 is in the unicharset, and cloud_font is non-negative, // displays the cloud feature of the char/font combination in green. // The canonical features are drawn first to show which ones have no // matches in the cloud features. // Until the features window is destroyed, each click in the features window // will display the samples that have that feature in a separate window. void MasterTrainer::DisplaySamples(const char* unichar_str1, int cloud_font, const char* unichar_str2, int canonical_font) { const IntFeatureMap& feature_map = feature_map_; const IntFeatureSpace& feature_space = feature_map.feature_space(); ScrollView* f_window = CreateFeatureSpaceWindow("Features", 100, 500); ClearFeatureSpaceWindow(norm_mode_ == NM_BASELINE ? baseline : character, f_window); int class_id2 = samples_.unicharset().unichar_to_id(unichar_str2); if (class_id2 != INVALID_UNICHAR_ID && canonical_font >= 0) { const TrainingSample* sample = samples_.GetCanonicalSample(canonical_font, class_id2); for (int f = 0; f < sample->num_features(); ++f) { RenderIntFeature(f_window, &sample->features()[f], ScrollView::RED); } } int class_id1 = samples_.unicharset().unichar_to_id(unichar_str1); if (class_id1 != INVALID_UNICHAR_ID && cloud_font >= 0) { const BitVector& cloud = samples_.GetCloudFeatures(cloud_font, class_id1); for (int f = 0; f < cloud.size(); ++f) { if (cloud[f]) { INT_FEATURE_STRUCT feature = feature_map.InverseIndexFeature(f); RenderIntFeature(f_window, &feature, ScrollView::GREEN); } } } f_window->Update(); ScrollView* s_window = CreateFeatureSpaceWindow("Samples", 100, 500); SVEventType ev_type; do { SVEvent* ev; // Wait until a click or popup event. ev = f_window->AwaitEvent(SVET_ANY); ev_type = ev->type; if (ev_type == SVET_CLICK) { int feature_index = feature_space.XYToFeatureIndex(ev->x, ev->y); if (feature_index >= 0) { // Iterate samples and display those with the feature. Shape shape; shape.AddToShape(class_id1, cloud_font); s_window->Clear(); samples_.DisplaySamplesWithFeature(feature_index, shape, feature_space, ScrollView::GREEN, s_window); s_window->Update(); } } delete ev; } while (ev_type != SVET_DESTROY); } #endif // GRAPHICS_DISABLED void MasterTrainer::TestClassifierVOld(bool replicate_samples, ShapeClassifier* test_classifier, ShapeClassifier* old_classifier) { SampleIterator sample_it; sample_it.Init(NULL, NULL, replicate_samples, &samples_); ErrorCounter::DebugNewErrors(test_classifier, old_classifier, CT_UNICHAR_TOPN_ERR, fontinfo_table_, page_images_, &sample_it); } // Tests the given test_classifier on the internal samples. // See TestClassifier for details. void MasterTrainer::TestClassifierOnSamples(CountTypes error_mode, int report_level, bool replicate_samples, ShapeClassifier* test_classifier, STRING* report_string) { TestClassifier(error_mode, report_level, replicate_samples, &samples_, test_classifier, report_string); } // Tests the given test_classifier on the given samples. // error_mode indicates what counts as an error. // report_levels: // 0 = no output. // 1 = bottom-line error rate. // 2 = bottom-line error rate + time. // 3 = font-level error rate + time. // 4 = list of all errors + short classifier debug output on 16 errors. // 5 = list of all errors + short classifier debug output on 25 errors. // If replicate_samples is true, then the test is run on an extended test // sample including replicated and systematically perturbed samples. // If report_string is non-NULL, a summary of the results for each font // is appended to the report_string. double MasterTrainer::TestClassifier(CountTypes error_mode, int report_level, bool replicate_samples, TrainingSampleSet* samples, ShapeClassifier* test_classifier, STRING* report_string) { SampleIterator sample_it; sample_it.Init(NULL, NULL, replicate_samples, samples); if (report_level > 0) { int num_samples = 0; for (sample_it.Begin(); !sample_it.AtEnd(); sample_it.Next()) ++num_samples; tprintf("Iterator has charset size of %d/%d, %d shapes, %d samples\n", sample_it.SparseCharsetSize(), sample_it.CompactCharsetSize(), test_classifier->GetShapeTable()->NumShapes(), num_samples); tprintf("Testing %sREPLICATED:\n", replicate_samples ? "" : "NON-"); } double unichar_error = 0.0; ErrorCounter::ComputeErrorRate(test_classifier, report_level, error_mode, fontinfo_table_, page_images_, &sample_it, &unichar_error, NULL, report_string); return unichar_error; } // Returns the average (in some sense) distance between the two given // shapes, which may contain multiple fonts and/or unichars. float MasterTrainer::ShapeDistance(const ShapeTable& shapes, int s1, int s2) { const IntFeatureMap& feature_map = feature_map_; const Shape& shape1 = shapes.GetShape(s1); const Shape& shape2 = shapes.GetShape(s2); int num_chars1 = shape1.size(); int num_chars2 = shape2.size(); float dist_sum = 0.0f; int dist_count = 0; if (num_chars1 > 1 || num_chars2 > 1) { // In the multi-char case try to optimize the calculation by computing // distances between characters of matching font where possible. for (int c1 = 0; c1 < num_chars1; ++c1) { for (int c2 = 0; c2 < num_chars2; ++c2) { dist_sum += samples_.UnicharDistance(shape1[c1], shape2[c2], true, feature_map); ++dist_count; } } } else { // In the single unichar case, there is little alternative, but to compute // the squared-order distance between pairs of fonts. dist_sum = samples_.UnicharDistance(shape1[0], shape2[0], false, feature_map); ++dist_count; } return dist_sum / dist_count; } // Replaces samples that are always fragmented with the corresponding // fragment samples. void MasterTrainer::ReplaceFragmentedSamples() { if (fragments_ == NULL) return; // Remove samples that are replaced by fragments. Each class that was // always naturally fragmented should be replaced by its fragments. int num_samples = samples_.num_samples(); for (int s = 0; s < num_samples; ++s) { TrainingSample* sample = samples_.mutable_sample(s); if (fragments_[sample->class_id()] > 0) samples_.KillSample(sample); } samples_.DeleteDeadSamples(); // Get ids of fragments in junk_samples_ that replace the dead chars. const UNICHARSET& frag_set = junk_samples_.unicharset(); #if 0 // TODO(rays) The original idea was to replace only graphemes that were // always naturally fragmented, but that left a lot of the Indic graphemes // out. Determine whether we can go back to that idea now that spacing // is fixed in the training images, or whether this code is obsolete. bool* good_junk = new bool[frag_set.size()]; memset(good_junk, 0, sizeof(*good_junk) * frag_set.size()); for (int dead_ch = 1; dead_ch < unicharset_.size(); ++dead_ch) { int frag_ch = fragments_[dead_ch]; if (frag_ch <= 0) continue; const char* frag_utf8 = frag_set.id_to_unichar(frag_ch); CHAR_FRAGMENT* frag = CHAR_FRAGMENT::parse_from_string(frag_utf8); // Mark the chars for all parts of the fragment as good in good_junk. for (int part = 0; part < frag->get_total(); ++part) { frag->set_pos(part); int good_ch = frag_set.unichar_to_id(frag->to_string().string()); if (good_ch != INVALID_UNICHAR_ID) good_junk[good_ch] = true; // We want this one. } } #endif // For now just use all the junk that was from natural fragments. // Get samples of fragments in junk_samples_ that replace the dead chars. int num_junks = junk_samples_.num_samples(); for (int s = 0; s < num_junks; ++s) { TrainingSample* sample = junk_samples_.mutable_sample(s); int junk_id = sample->class_id(); const char* frag_utf8 = frag_set.id_to_unichar(junk_id); CHAR_FRAGMENT* frag = CHAR_FRAGMENT::parse_from_string(frag_utf8); if (frag != NULL && frag->is_natural()) { junk_samples_.extract_sample(s); samples_.AddSample(frag_set.id_to_unichar(junk_id), sample); } } junk_samples_.DeleteDeadSamples(); junk_samples_.OrganizeByFontAndClass(); samples_.OrganizeByFontAndClass(); unicharset_.clear(); unicharset_.AppendOtherUnicharset(samples_.unicharset()); // delete [] good_junk; // Fragments_ no longer needed? delete [] fragments_; fragments_ = NULL; } // Runs a hierarchical agglomerative clustering to merge shapes in the given // shape_table, while satisfying the given constraints: // * End with at least min_shapes left in shape_table, // * No shape shall have more than max_shape_unichars in it, // * Don't merge shapes where the distance between them exceeds max_dist. const float kInfiniteDist = 999.0f; void MasterTrainer::ClusterShapes(int min_shapes, int max_shape_unichars, float max_dist, ShapeTable* shapes) { int num_shapes = shapes->NumShapes(); int max_merges = num_shapes - min_shapes; GenericVector* shape_dists = new GenericVector[num_shapes]; float min_dist = kInfiniteDist; int min_s1 = 0; int min_s2 = 0; tprintf("Computing shape distances..."); for (int s1 = 0; s1 < num_shapes; ++s1) { for (int s2 = s1 + 1; s2 < num_shapes; ++s2) { ShapeDist dist(s1, s2, ShapeDistance(*shapes, s1, s2)); shape_dists[s1].push_back(dist); if (dist.distance < min_dist) { min_dist = dist.distance; min_s1 = s1; min_s2 = s2; } } tprintf(" %d", s1); } tprintf("\n"); int num_merged = 0; while (num_merged < max_merges && min_dist < max_dist) { tprintf("Distance = %f: ", min_dist); int num_unichars = shapes->MergedUnicharCount(min_s1, min_s2); shape_dists[min_s1][min_s2 - min_s1 - 1].distance = kInfiniteDist; if (num_unichars > max_shape_unichars) { tprintf("Merge of %d and %d with %d would exceed max of %d unichars\n", min_s1, min_s2, num_unichars, max_shape_unichars); } else { shapes->MergeShapes(min_s1, min_s2); shape_dists[min_s2].clear(); ++num_merged; for (int s = 0; s < min_s1; ++s) { if (!shape_dists[s].empty()) { shape_dists[s][min_s1 - s - 1].distance = ShapeDistance(*shapes, s, min_s1); shape_dists[s][min_s2 - s -1].distance = kInfiniteDist; } } for (int s2 = min_s1 + 1; s2 < num_shapes; ++s2) { if (shape_dists[min_s1][s2 - min_s1 - 1].distance < kInfiniteDist) shape_dists[min_s1][s2 - min_s1 - 1].distance = ShapeDistance(*shapes, min_s1, s2); } for (int s = min_s1 + 1; s < min_s2; ++s) { if (!shape_dists[s].empty()) { shape_dists[s][min_s2 - s - 1].distance = kInfiniteDist; } } } min_dist = kInfiniteDist; for (int s1 = 0; s1 < num_shapes; ++s1) { for (int i = 0; i < shape_dists[s1].size(); ++i) { if (shape_dists[s1][i].distance < min_dist) { min_dist = shape_dists[s1][i].distance; min_s1 = s1; min_s2 = s1 + 1 + i; } } } } tprintf("Stopped with %d merged, min dist %f\n", num_merged, min_dist); delete [] shape_dists; if (debug_level_ > 1) { for (int s1 = 0; s1 < num_shapes; ++s1) { if (shapes->MasterDestinationIndex(s1) == s1) { tprintf("Master shape:%s\n", shapes->DebugStr(s1).string()); } } } } } // namespace tesseract. tesseract-3.04.01/classify/mastertrainer.h000066400000000000000000000321551266071204500205330ustar00rootroot00000000000000// Copyright 2010 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: mastertrainer.h // Description: Trainer to build the MasterClassifier. // Author: Ray Smith // Created: Wed Nov 03 18:07:01 PDT 2010 // // (C) Copyright 2010, 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_MASTERTRAINER_H__ #define TESSERACT_TRAINING_MASTERTRAINER_H__ /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "classify.h" #include "cluster.h" #include "intfx.h" #include "elst.h" #include "errorcounter.h" #include "featdefs.h" #include "fontinfo.h" #include "indexmapbidi.h" #include "intfeaturespace.h" #include "intfeaturemap.h" #include "intmatcher.h" #include "params.h" #include "shapetable.h" #include "trainingsample.h" #include "trainingsampleset.h" #include "unicharset.h" namespace tesseract { class ShapeClassifier; // Simple struct to hold the distance between two shapes during clustering. struct ShapeDist { ShapeDist() : shape1(0), shape2(0), distance(0.0f) {} ShapeDist(int s1, int s2, float dist) : shape1(s1), shape2(s2), distance(dist) {} // Sort operator to sort in ascending order of distance. bool operator<(const ShapeDist& other) const { return distance < other.distance; } int shape1; int shape2; float distance; }; // Class to encapsulate training processes that use the TrainingSampleSet. // Initially supports shape clustering and mftrainining. // Other important features of the MasterTrainer are conditioning the data // by outlier elimination, replication with perturbation, and serialization. class MasterTrainer { public: MasterTrainer(NormalizationMode norm_mode, bool shape_analysis, bool replicate_samples, int debug_level); ~MasterTrainer(); // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); // Loads an initial unicharset, or sets one up if the file cannot be read. void LoadUnicharset(const char* filename); // Sets the feature space definition. void SetFeatureSpace(const IntFeatureSpace& fs) { feature_space_ = fs; feature_map_.Init(fs); } // Reads the samples and their features from the given file, // adding them to the trainer with the font_id from the content of the file. // If verification, then these are verification samples, not training. void ReadTrainingSamples(const char* page_name, const FEATURE_DEFS_STRUCT& feature_defs, bool verification); // Adds the given single sample to the trainer, setting the classid // appropriately from the given unichar_str. void AddSample(bool verification, const char* unichar_str, TrainingSample* sample); // Loads all pages from the given tif filename and append to page_images_. // Must be called after ReadTrainingSamples, as the current number of images // is used as an offset for page numbers in the samples. void LoadPageImages(const char* filename); // Cleans up the samples after initial load from the tr files, and prior to // saving the MasterTrainer: // Remaps fragmented chars if running shape anaylsis. // Sets up the samples appropriately for class/fontwise access. // Deletes outlier samples. void PostLoadCleanup(); // Gets the samples ready for training. Use after both // ReadTrainingSamples+PostLoadCleanup or DeSerialize. // Re-indexes the features and computes canonical and cloud features. void PreTrainingSetup(); // Sets up the master_shapes_ table, which tells which fonts should stay // together until they get to a leaf node classifier. void SetupMasterShapes(); // Adds the junk_samples_ to the main samples_ set. Junk samples are initially // fragments and n-grams (all incorrectly segmented characters). // Various training functions may result in incorrectly segmented characters // being added to the unicharset of the main samples, perhaps because they // form a "radical" decomposition of some (Indic) grapheme, or because they // just look the same as a real character (like rn/m) // This function moves all the junk samples, to the main samples_ set, but // desirable junk, being any sample for which the unichar already exists in // the samples_ unicharset gets the unichar-ids re-indexed to match, but // anything else gets re-marked as unichar_id 0 (space character) to identify // it as junk to the error counter. void IncludeJunk(); // Replicates the samples and perturbs them if the enable_replication_ flag // is set. MUST be used after the last call to OrganizeByFontAndClass on // the training samples, ie after IncludeJunk if it is going to be used, as // OrganizeByFontAndClass will eat the replicated samples into the regular // samples. void ReplicateAndRandomizeSamplesIfRequired(); // Loads the basic font properties file into fontinfo_table_. // Returns false on failure. bool LoadFontInfo(const char* filename); // Loads the xheight font properties file into xheights_. // Returns false on failure. bool LoadXHeights(const char* filename); // Reads spacing stats from filename and adds them to fontinfo_table. // Returns false on failure. bool AddSpacingInfo(const char *filename); // Returns the font id corresponding to the given font name. // Returns -1 if the font cannot be found. int GetFontInfoId(const char* font_name); // Returns the font_id of the closest matching font name to the given // filename. It is assumed that a substring of the filename will match // one of the fonts. If more than one is matched, the longest is returned. int GetBestMatchingFontInfoId(const char* filename); // Returns the filename of the tr file corresponding to the command-line // argument with the given index. const STRING& GetTRFileName(int index) const { return tr_filenames_[index]; } // Sets up a flat shapetable with one shape per class/font combination. void SetupFlatShapeTable(ShapeTable* shape_table); // Sets up a Clusterer for mftraining on a single shape_id. // Call FreeClusterer on the return value after use. CLUSTERER* SetupForClustering(const ShapeTable& shape_table, const FEATURE_DEFS_STRUCT& feature_defs, int shape_id, int* num_samples); // Writes the given float_classes (produced by SetupForFloat2Int) as inttemp // to the given inttemp_file, and the corresponding pffmtable. // The unicharset is the original encoding of graphemes, and shape_set should // match the size of the shape_table, and may possibly be totally fake. void WriteInttempAndPFFMTable(const UNICHARSET& unicharset, const UNICHARSET& shape_set, const ShapeTable& shape_table, CLASS_STRUCT* float_classes, const char* inttemp_file, const char* pffmtable_file); const UNICHARSET& unicharset() const { return samples_.unicharset(); } TrainingSampleSet* GetSamples() { return &samples_; } const ShapeTable& master_shapes() const { return master_shapes_; } // Generates debug output relating to the canonical distance between the // two given UTF8 grapheme strings. void DebugCanonical(const char* unichar_str1, const char* unichar_str2); #ifndef GRAPHICS_DISABLED // Debugging for cloud/canonical features. // Displays a Features window containing: // If unichar_str2 is in the unicharset, and canonical_font is non-negative, // displays the canonical features of the char/font combination in red. // If unichar_str1 is in the unicharset, and cloud_font is non-negative, // displays the cloud feature of the char/font combination in green. // The canonical features are drawn first to show which ones have no // matches in the cloud features. // Until the features window is destroyed, each click in the features window // will display the samples that have that feature in a separate window. void DisplaySamples(const char* unichar_str1, int cloud_font, const char* unichar_str2, int canonical_font); #endif // GRAPHICS_DISABLED void TestClassifierVOld(bool replicate_samples, ShapeClassifier* test_classifier, ShapeClassifier* old_classifier); // Tests the given test_classifier on the internal samples. // See TestClassifier for details. void TestClassifierOnSamples(CountTypes error_mode, int report_level, bool replicate_samples, ShapeClassifier* test_classifier, STRING* report_string); // Tests the given test_classifier on the given samples // error_mode indicates what counts as an error. // report_levels: // 0 = no output. // 1 = bottom-line error rate. // 2 = bottom-line error rate + time. // 3 = font-level error rate + time. // 4 = list of all errors + short classifier debug output on 16 errors. // 5 = list of all errors + short classifier debug output on 25 errors. // If replicate_samples is true, then the test is run on an extended test // sample including replicated and systematically perturbed samples. // If report_string is non-NULL, a summary of the results for each font // is appended to the report_string. double TestClassifier(CountTypes error_mode, int report_level, bool replicate_samples, TrainingSampleSet* samples, ShapeClassifier* test_classifier, STRING* report_string); // Returns the average (in some sense) distance between the two given // shapes, which may contain multiple fonts and/or unichars. // This function is public to facilitate testing. float ShapeDistance(const ShapeTable& shapes, int s1, int s2); private: // Replaces samples that are always fragmented with the corresponding // fragment samples. void ReplaceFragmentedSamples(); // Runs a hierarchical agglomerative clustering to merge shapes in the given // shape_table, while satisfying the given constraints: // * End with at least min_shapes left in shape_table, // * No shape shall have more than max_shape_unichars in it, // * Don't merge shapes where the distance between them exceeds max_dist. void ClusterShapes(int min_shapes, int max_shape_unichars, float max_dist, ShapeTable* shape_table); private: NormalizationMode norm_mode_; // Character set we are training for. UNICHARSET unicharset_; // Original feature space. Subspace mapping is contained in feature_map_. IntFeatureSpace feature_space_; TrainingSampleSet samples_; TrainingSampleSet junk_samples_; TrainingSampleSet verify_samples_; // Master shape table defines what fonts stay together until the leaves. ShapeTable master_shapes_; // Flat shape table has each unichar/font id pair in a separate shape. ShapeTable flat_shapes_; // Font metrics gathered from multiple files. FontInfoTable fontinfo_table_; // Array of xheights indexed by font ids in fontinfo_table_; GenericVector xheights_; // Non-serialized data initialized by other means or used temporarily // during loading of training samples. // Number of different class labels in unicharset_. int charsetsize_; // Flag to indicate that we are running shape analysis and need fragments // fixing. bool enable_shape_anaylsis_; // Flag to indicate that sample replication is required. bool enable_replication_; // Array of classids of fragments that replace the correctly segmented chars. int* fragments_; // Classid of previous correctly segmented sample that was added. int prev_unichar_id_; // Debug output control. int debug_level_; // Feature map used to construct reduced feature spaces for compact // classifiers. IntFeatureMap feature_map_; // Vector of Pix pointers used for classifiers that need the image. // Indexed by page_num_ in the samples. // These images are owned by the trainer and need to be pixDestroyed. GenericVector page_images_; // Vector of filenames of loaded tr files. GenericVector tr_filenames_; }; } // namespace tesseract. #endif tesseract-3.04.01/classify/mf.cpp000066400000000000000000000062731266071204500166120ustar00rootroot00000000000000/****************************************************************************** ** Filename: mf.c ** Purpose: Micro-feature interface to flexible feature extractor. ** Author: Dan Johnson ** History: Thu May 24 09:08:38 1990, 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 "mf.h" #include "featdefs.h" #include "mfdefs.h" #include "mfx.h" #include /*---------------------------------------------------------------------------- Global Data Definitions and Declarations ----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------- Private Code ----------------------------------------------------------------------------*/ /** * Call the old micro-feature extractor and then copy * the features into the new format. Then deallocate the * old micro-features. * @param Blob blob to extract micro-features from * @param cn_denorm control parameter to feature extractor. * @return Micro-features for Blob. * @note Exceptions: none * @note History: Wed May 23 18:06:38 1990, DSJ, Created. */ FEATURE_SET ExtractMicros(TBLOB* Blob, const DENORM& cn_denorm) { int NumFeatures; MICROFEATURES Features, OldFeatures; FEATURE_SET FeatureSet; FEATURE Feature; MICROFEATURE OldFeature; OldFeatures = BlobMicroFeatures(Blob, cn_denorm); if (OldFeatures == NULL) return NULL; NumFeatures = count (OldFeatures); FeatureSet = NewFeatureSet (NumFeatures); Features = OldFeatures; iterate(Features) { OldFeature = (MICROFEATURE) first_node (Features); Feature = NewFeature (&MicroFeatureDesc); Feature->Params[MFDirection] = OldFeature[ORIENTATION]; Feature->Params[MFXPosition] = OldFeature[XPOSITION]; Feature->Params[MFYPosition] = OldFeature[YPOSITION]; Feature->Params[MFLength] = OldFeature[MFLENGTH]; // Bulge features are deprecated and should not be used. Set to 0. Feature->Params[MFBulge1] = 0.0f; Feature->Params[MFBulge2] = 0.0f; #ifndef _WIN32 // Assert that feature parameters are well defined. int i; for (i = 0; i < Feature->Type->NumParams; i++) { ASSERT_HOST(!isnan(Feature->Params[i])); } #endif AddFeature(FeatureSet, Feature); } FreeMicroFeatures(OldFeatures); return FeatureSet; } /* ExtractMicros */ tesseract-3.04.01/classify/mf.h000066400000000000000000000032051266071204500162470ustar00rootroot00000000000000/****************************************************************************** ** Filename: mf.h ** Purpose: Micro-feature interface to flexible feature extractor. ** Author: Dan Johnson ** History: Thu May 24 09:39:56 1990, 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 MF_H #define MF_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "ocrfeatures.h" #include "blobs.h" typedef enum { MFXPosition, MFYPosition, MFLength, MFDirection, MFBulge1, MFBulge2, MFCount // For array sizes. } MF_PARAM_NAME; typedef float MicroFeature[MFCount]; /*---------------------------------------------------------------------------- Private Function Prototypes -----------------------------------------------------------------------------*/ FEATURE_SET ExtractMicros(TBLOB* Blob, const DENORM& cn_denorm); #endif tesseract-3.04.01/classify/mfdefs.cpp000066400000000000000000000042441266071204500174500ustar00rootroot00000000000000/****************************************************************************** ** Filename: mfdefs.c ** Purpose: Basic routines for manipulating micro-features ** Author: Dan Johnson ** History: Mon Jan 22 08:48:58 1990, 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 "mfdefs.h" #include "emalloc.h" #include /*---------------------------------------------------------------------------- Public Code ----------------------------------------------------------------------------**/ /*---------------------------------------------------------------------------*/ /** * This routine allocates and returns a new micro-feature * data structure. * @return New MICROFEATURE * @note History: 7/27/89, DSJ, Created. */ MICROFEATURE NewMicroFeature() { return ((MICROFEATURE) Emalloc (sizeof (MFBLOCK))); } /* NewMicroFeature */ /*---------------------------------------------------------------------------*/ /** * This routine deallocates all of the memory consumed by * a list of micro-features. * @param MicroFeatures list of micro-features to be freed * @return none * @note History: 7/27/89, DSJ, Created. */ void FreeMicroFeatures(MICROFEATURES MicroFeatures) { destroy_nodes(MicroFeatures, Efree); } /* FreeMicroFeatures */ tesseract-3.04.01/classify/mfdefs.h000066400000000000000000000042421266071204500171130ustar00rootroot00000000000000/****************************************************************************** ** Filename: mfdefs.h ** Purpose: Definition of micro-features ** Author: Dan Johnson ** History: Mon Jan 22 08:42:13 1990, 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 MFDEFS_H #define MFDEFS_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "oldlist.h" #include "matchdefs.h" /* definition of a list of micro-features */ typedef LIST MICROFEATURES; /* definition of structure of micro-features */ #define MFSIZE 6 typedef FLOAT32 MFBLOCK[MFSIZE]; typedef FLOAT32 *MICROFEATURE; /* definitions of individual micro-feature parameters */ #define XPOSITION 0 #define YPOSITION 1 #define MFLENGTH 2 #define ORIENTATION 3 #define FIRSTBULGE 4 #define SECONDBULGE 5 /**---------------------------------------------------------------------------- Macros ----------------------------------------------------------------------------**/ /* macros for accessing micro-feature lists */ #define NextFeatureOf(L) ( (MICROFEATURE) first_node ( L ) ) /**---------------------------------------------------------------------------- Public Function Prototypes ----------------------------------------------------------------------------**/ MICROFEATURE NewMicroFeature(); void FreeMicroFeatures(MICROFEATURES MicroFeatures); #endif tesseract-3.04.01/classify/mfoutline.cpp000066400000000000000000000372221266071204500202100ustar00rootroot00000000000000/****************************************************************************** ** Filename: mfoutline.c ** Purpose: Interface to outline struct used for extracting features ** Author: Dan Johnson ** History: Thu May 17 08:14:18 1990, 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 "clusttool.h" //If remove you get cought in a loop somewhere #include "emalloc.h" #include "mfoutline.h" #include "blobs.h" #include "const.h" #include "mfx.h" #include "params.h" #include "classify.h" #include #include /*---------------------------------------------------------------------------- Public Code ----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /** Convert a blob into a list of MFOUTLINEs (float-based microfeature format). */ LIST ConvertBlob(TBLOB *blob) { LIST outlines = NIL_LIST; return (blob == NULL) ? NIL_LIST : ConvertOutlines(blob->outlines, outlines, outer); } /*---------------------------------------------------------------------------*/ /** Convert a TESSLINE into the float-based MFOUTLINE micro-feature format. */ MFOUTLINE ConvertOutline(TESSLINE *outline) { MFEDGEPT *NewPoint; MFOUTLINE MFOutline = NIL_LIST; EDGEPT *EdgePoint; EDGEPT *StartPoint; EDGEPT *NextPoint; if (outline == NULL || outline->loop == NULL) return MFOutline; StartPoint = outline->loop; EdgePoint = StartPoint; do { NextPoint = EdgePoint->next; /* filter out duplicate points */ if (EdgePoint->pos.x != NextPoint->pos.x || EdgePoint->pos.y != NextPoint->pos.y) { NewPoint = NewEdgePoint(); ClearMark(NewPoint); NewPoint->Hidden = EdgePoint->IsHidden(); NewPoint->Point.x = EdgePoint->pos.x; NewPoint->Point.y = EdgePoint->pos.y; MFOutline = push(MFOutline, NewPoint); } EdgePoint = NextPoint; } while (EdgePoint != StartPoint); if (MFOutline != NULL) MakeOutlineCircular(MFOutline); return MFOutline; } /*---------------------------------------------------------------------------*/ /** * Convert a tree of outlines to a list of MFOUTLINEs (lists of MFEDGEPTs). * * @param outline first outline to be converted * @param mf_outlines list to add converted outlines to * @param outline_type are the outlines outer or holes? */ LIST ConvertOutlines(TESSLINE *outline, LIST mf_outlines, OUTLINETYPE outline_type) { MFOUTLINE mf_outline; while (outline != NULL) { mf_outline = ConvertOutline(outline); if (mf_outline != NULL) mf_outlines = push(mf_outlines, mf_outline); outline = outline->next; } return mf_outlines; } /*---------------------------------------------------------------------------*/ /** * This routine searches through the specified outline, computes * a slope for each vector in the outline, and marks each * vector as having one of the following directions: * N, S, E, W, NE, NW, SE, SW * This information is then stored in the outline and the * outline is returned. * @param Outline micro-feature outline to analyze * @param MinSlope controls "snapping" of segments to horizontal * @param MaxSlope controls "snapping" of segments to vertical * @return none * @note Exceptions: none * @note History: 7/21/89, DSJ, Created. */ void FindDirectionChanges(MFOUTLINE Outline, FLOAT32 MinSlope, FLOAT32 MaxSlope) { MFEDGEPT *Current; MFEDGEPT *Last; MFOUTLINE EdgePoint; if (DegenerateOutline (Outline)) return; Last = PointAt (Outline); Outline = NextPointAfter (Outline); EdgePoint = Outline; do { Current = PointAt (EdgePoint); ComputeDirection(Last, Current, MinSlope, MaxSlope); Last = Current; EdgePoint = NextPointAfter (EdgePoint); } while (EdgePoint != Outline); } /* FindDirectionChanges */ /*---------------------------------------------------------------------------*/ /** * This routine deallocates all of the memory consumed by * a micro-feature outline. * @param arg micro-feature outline to be freed * @return none * @note Exceptions: none * @note History: 7/27/89, DSJ, Created. */ void FreeMFOutline(void *arg) { //MFOUTLINE Outline) MFOUTLINE Start; MFOUTLINE Outline = (MFOUTLINE) arg; /* break the circular outline so we can use std. techniques to deallocate */ Start = list_rest (Outline); set_rest(Outline, NIL_LIST); while (Start != NULL) { free_struct (first_node (Start), sizeof (MFEDGEPT), "MFEDGEPT"); Start = pop (Start); } } /* FreeMFOutline */ /*---------------------------------------------------------------------------*/ /** * Release all memory consumed by the specified list * of outlines. * @param Outlines list of mf-outlines to be freed * @return none * @note Exceptions: none * @note History: Thu Dec 13 16:14:50 1990, DSJ, Created. */ void FreeOutlines(LIST Outlines) { destroy_nodes(Outlines, FreeMFOutline); } /* FreeOutlines */ /*---------------------------------------------------------------------------*/ /** * This routine searches through the specified outline and finds * the points at which the outline changes direction. These * points are then marked as "extremities". This routine is * used as an alternative to FindExtremities(). It forces the * endpoints of the microfeatures to be at the direction * changes rather than at the midpoint between direction * changes. * @param Outline micro-feature outline to analyze * @return none * @note Globals: none * @note Exceptions: none * @note History: 6/29/90, DSJ, Created. */ void MarkDirectionChanges(MFOUTLINE Outline) { MFOUTLINE Current; MFOUTLINE Last; MFOUTLINE First; if (DegenerateOutline (Outline)) return; First = NextDirectionChange (Outline); Last = First; do { Current = NextDirectionChange (Last); MarkPoint (PointAt (Current)); Last = Current; } while (Last != First); } /* MarkDirectionChanges */ /*---------------------------------------------------------------------------*/ /** Return a new edge point for a micro-feature outline. */ MFEDGEPT *NewEdgePoint() { return ((MFEDGEPT *) alloc_struct(sizeof(MFEDGEPT), "MFEDGEPT")); } /*---------------------------------------------------------------------------*/ /** * This routine returns the next point in the micro-feature * outline that is an extremity. The search starts after * EdgePoint. The routine assumes that the outline being * searched is not a degenerate outline (i.e. it must have * 2 or more edge points). * @param EdgePoint start search from this point * @return Next extremity in the outline after EdgePoint. * @note Globals: none * @note Exceptions: none * @note History: 7/26/89, DSJ, Created. */ MFOUTLINE NextExtremity(MFOUTLINE EdgePoint) { EdgePoint = NextPointAfter(EdgePoint); while (!PointAt(EdgePoint)->ExtremityMark) EdgePoint = NextPointAfter(EdgePoint); return (EdgePoint); } /* NextExtremity */ /*---------------------------------------------------------------------------*/ /** * This routine normalizes the coordinates of the specified * outline so that the outline is deskewed down to the * baseline, translated so that x=0 is at XOrigin, and scaled * so that the height of a character cell from descender to * ascender is 1. Of this height, 0.25 is for the descender, * 0.25 for the ascender, and 0.5 for the x-height. The * y coordinate of the baseline is 0. * @param Outline outline to be normalized * @param XOrigin x-origin of text * @return none * @note Globals: none * @note Exceptions: none * @note History: 8/2/89, DSJ, Created. */ void NormalizeOutline(MFOUTLINE Outline, FLOAT32 XOrigin) { if (Outline == NIL_LIST) return; MFOUTLINE EdgePoint = Outline; do { MFEDGEPT *Current = PointAt(EdgePoint); Current->Point.y = MF_SCALE_FACTOR * (Current->Point.y - kBlnBaselineOffset); Current->Point.x = MF_SCALE_FACTOR * (Current->Point.x - XOrigin); EdgePoint = NextPointAfter(EdgePoint); } while (EdgePoint != Outline); } /* NormalizeOutline */ /*---------------------------------------------------------------------------*/ namespace tesseract { /** * This routine normalizes every outline in Outlines * according to the currently selected normalization method. * It also returns the scale factors that it used to do this * scaling. The scale factors returned represent the x and * y sizes in the normalized coordinate system that correspond * to 1 pixel in the original coordinate system. * * Globals: * - classify_norm_method method being used for normalization * - classify_char_norm_range map radius of gyration to this value * @param Outlines list of outlines to be normalized * @param XScale x-direction scale factor used by routine * @param YScale y-direction scale factor used by routine * @return none (Outlines are changed and XScale and YScale are updated) * @note Exceptions: none * @note History: Fri Dec 14 08:14:55 1990, DSJ, Created. */ void Classify::NormalizeOutlines(LIST Outlines, FLOAT32 *XScale, FLOAT32 *YScale) { MFOUTLINE Outline; switch (classify_norm_method) { case character: ASSERT_HOST(!"How did NormalizeOutlines get called in character mode?"); break; case baseline: iterate(Outlines) { Outline = (MFOUTLINE) first_node(Outlines); NormalizeOutline(Outline, 0.0); } *XScale = *YScale = MF_SCALE_FACTOR; break; } } /* NormalizeOutlines */ } // namespace tesseract /*---------------------------------------------------------------------------- Private Code ----------------------------------------------------------------------------*/ /** * Change the direction of every vector in the specified * outline segment to Direction. The segment to be changed * starts at Start and ends at End. Note that the previous * direction of End must also be changed to reflect the * change in direction of the point before it. * @param Start, End defines segment of outline to be modified * @param Direction new direction to assign to segment * @return none * @note Globals: none * @note Exceptions: none * @note History: Fri May 4 10:42:04 1990, DSJ, Created. */ void ChangeDirection(MFOUTLINE Start, MFOUTLINE End, DIRECTION Direction) { MFOUTLINE Current; for (Current = Start; Current != End; Current = NextPointAfter (Current)) PointAt (Current)->Direction = Direction; PointAt (End)->PreviousDirection = Direction; } /* ChangeDirection */ /** * This routine normalizes each point in Outline by * translating it to the specified center and scaling it * anisotropically according to the given scale factors. * @param Outline outline to be character normalized * @param cn_denorm * @return none * @note Globals: none * @note Exceptions: none * @note History: Fri Dec 14 10:27:11 1990, DSJ, Created. */ void CharNormalizeOutline(MFOUTLINE Outline, const DENORM& cn_denorm) { MFOUTLINE First, Current; MFEDGEPT *CurrentPoint; if (Outline == NIL_LIST) return; First = Outline; Current = First; do { CurrentPoint = PointAt(Current); FCOORD pos(CurrentPoint->Point.x, CurrentPoint->Point.y); cn_denorm.LocalNormTransform(pos, &pos); CurrentPoint->Point.x = (pos.x() - MAX_UINT8 / 2) * MF_SCALE_FACTOR; CurrentPoint->Point.y = (pos.y() - MAX_UINT8 / 2) * MF_SCALE_FACTOR; Current = NextPointAfter(Current); } while (Current != First); } /* CharNormalizeOutline */ /** * This routine computes the slope from Start to Finish and * and then computes the approximate direction of the line * segment from Start to Finish. The direction is quantized * into 8 buckets: * N, S, E, W, NE, NW, SE, SW * Both the slope and the direction are then stored into * the appropriate fields of the Start edge point. The * direction is also stored into the PreviousDirection field * of the Finish edge point. * @param Start starting point to compute direction from * @param Finish finishing point to compute direction to * @param MinSlope slope below which lines are horizontal * @param MaxSlope slope above which lines are vertical * @return none * @note Globals: none * @note Exceptions: none * @note History: 7/25/89, DSJ, Created. */ void ComputeDirection(MFEDGEPT *Start, MFEDGEPT *Finish, FLOAT32 MinSlope, FLOAT32 MaxSlope) { FVECTOR Delta; Delta.x = Finish->Point.x - Start->Point.x; Delta.y = Finish->Point.y - Start->Point.y; if (Delta.x == 0) if (Delta.y < 0) { Start->Slope = -MAX_FLOAT32; Start->Direction = south; } else { Start->Slope = MAX_FLOAT32; Start->Direction = north; } else { Start->Slope = Delta.y / Delta.x; if (Delta.x > 0) if (Delta.y > 0) if (Start->Slope > MinSlope) if (Start->Slope < MaxSlope) Start->Direction = northeast; else Start->Direction = north; else Start->Direction = east; else if (Start->Slope < -MinSlope) if (Start->Slope > -MaxSlope) Start->Direction = southeast; else Start->Direction = south; else Start->Direction = east; else if (Delta.y > 0) if (Start->Slope < -MinSlope) if (Start->Slope > -MaxSlope) Start->Direction = northwest; else Start->Direction = north; else Start->Direction = west; else if (Start->Slope > MinSlope) if (Start->Slope < MaxSlope) Start->Direction = southwest; else Start->Direction = south; else Start->Direction = west; } Finish->PreviousDirection = Start->Direction; } /** * This routine returns the next point in the micro-feature * outline that has a direction different than EdgePoint. The * routine assumes that the outline being searched is not a * degenerate outline (i.e. it must have 2 or more edge points). * @param EdgePoint start search from this point * @return Point of next direction change in micro-feature outline. * @note Globals: none * @note Exceptions: none * @note History: 7/25/89, DSJ, Created. */ MFOUTLINE NextDirectionChange(MFOUTLINE EdgePoint) { DIRECTION InitialDirection; InitialDirection = PointAt (EdgePoint)->Direction; MFOUTLINE next_pt = NULL; do { EdgePoint = NextPointAfter(EdgePoint); next_pt = NextPointAfter(EdgePoint); } while (PointAt(EdgePoint)->Direction == InitialDirection && !PointAt(EdgePoint)->Hidden && next_pt != NULL && !PointAt(next_pt)->Hidden); return (EdgePoint); } tesseract-3.04.01/classify/mfoutline.h000066400000000000000000000103301266071204500176440ustar00rootroot00000000000000/****************************************************************************** ** Filename: mfoutline.h ** Purpose: Interface spec for fx outline structures ** Author: Dan Johnson ** History: Thu May 17 08:55:32 1990, 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 MFOUTLINE_H #define MFOUTLINE_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "blobs.h" #include "host.h" #include "oldlist.h" #include "fpoint.h" #include "params.h" #define NORMAL_X_HEIGHT (0.5) #define NORMAL_BASELINE (0.0) typedef LIST MFOUTLINE; typedef enum { north, south, east, west, northeast, northwest, southeast, southwest } DIRECTION; typedef struct { FPOINT Point; FLOAT32 Slope; unsigned Padding:20; BOOL8 Hidden:TRUE; BOOL8 ExtremityMark:TRUE; DIRECTION Direction:4; DIRECTION PreviousDirection:4; } MFEDGEPT; typedef enum { outer, hole } OUTLINETYPE; typedef enum { baseline, character } NORM_METHOD; /**---------------------------------------------------------------------------- Macros ----------------------------------------------------------------------------**/ #define AverageOf(A,B) (((A) + (B)) / 2) /* macro for computing the scale factor to use to normalize characters */ #define MF_SCALE_FACTOR (NORMAL_X_HEIGHT / kBlnXHeight) /* macros for manipulating micro-feature outlines */ #define DegenerateOutline(O) (((O) == NIL_LIST) || ((O) == list_rest(O))) #define PointAt(O) ((MFEDGEPT *) first_node (O)) #define NextPointAfter(E) (list_rest (E)) #define MakeOutlineCircular(O) (set_rest (last (O), (O))) /* macros for manipulating micro-feature outline edge points */ #define ClearMark(P) ((P)->ExtremityMark = FALSE) #define MarkPoint(P) ((P)->ExtremityMark = TRUE) /**---------------------------------------------------------------------------- Public Function Prototypes ----------------------------------------------------------------------------**/ void ComputeBlobCenter(TBLOB *Blob, TPOINT *BlobCenter); LIST ConvertBlob(TBLOB *Blob); MFOUTLINE ConvertOutline(TESSLINE *Outline); LIST ConvertOutlines(TESSLINE *Outline, LIST ConvertedOutlines, OUTLINETYPE OutlineType); void FilterEdgeNoise(MFOUTLINE Outline, FLOAT32 NoiseSegmentLength); void FindDirectionChanges(MFOUTLINE Outline, FLOAT32 MinSlope, FLOAT32 MaxSlope); void FreeMFOutline(void *agr); //MFOUTLINE Outline); void FreeOutlines(LIST Outlines); void MarkDirectionChanges(MFOUTLINE Outline); MFEDGEPT *NewEdgePoint(); MFOUTLINE NextExtremity(MFOUTLINE EdgePoint); void NormalizeOutline(MFOUTLINE Outline, FLOAT32 XOrigin); /*---------------------------------------------------------------------------- Private Function Prototypes -----------------------------------------------------------------------------*/ void ChangeDirection(MFOUTLINE Start, MFOUTLINE End, DIRECTION Direction); // Normalizes the Outline in-place using cn_denorm's local transformation, // then converts from the integer feature range [0,255] to the clusterer // feature range of [-0.5, 0.5]. void CharNormalizeOutline(MFOUTLINE Outline, const DENORM& cn_denorm); void ComputeDirection(MFEDGEPT *Start, MFEDGEPT *Finish, FLOAT32 MinSlope, FLOAT32 MaxSlope); MFOUTLINE NextDirectionChange(MFOUTLINE EdgePoint); #endif tesseract-3.04.01/classify/mfx.cpp000066400000000000000000000170041266071204500167740ustar00rootroot00000000000000/****************************************************************************** ** Filename: mfx.c ** Purpose: Micro feature extraction routines ** Author: Dan Johnson ** History: 7/21/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 "mfdefs.h" #include "mfoutline.h" #include "clusttool.h" //NEEDED #include "const.h" #include "intfx.h" #include "normalis.h" #include "params.h" #include /*---------------------------------------------------------------------------- Variables ----------------------------------------------------------------------------*/ /* old numbers corresponded to 10.0 degrees and 80.0 degrees */ double_VAR(classify_min_slope, 0.414213562, "Slope below which lines are called horizontal"); double_VAR(classify_max_slope, 2.414213562, "Slope above which lines are called vertical"); /*---------------------------------------------------------------------------- Macros ----------------------------------------------------------------------------*/ /* miscellaneous macros */ #define NormalizeAngle(A) ( (((A)<0)?((A)+2*PI):(A)) / (2*PI) ) /*---------------------------------------------------------------------------- Private Function Prototypes -----------------------------------------------------------------------------*/ FLOAT32 ComputeOrientation(MFEDGEPT *Start, MFEDGEPT *End); MICROFEATURES ConvertToMicroFeatures(MFOUTLINE Outline, MICROFEATURES MicroFeatures); MICROFEATURE ExtractMicroFeature(MFOUTLINE Start, MFOUTLINE End); /*---------------------------------------------------------------------------- Public Code ----------------------------------------------------------------------------*/ /** * This routine extracts micro-features from the specified * blob and returns a list of the micro-features. All * micro-features are normalized according to the specified * line statistics. * @param Blob blob to extract micro-features from * @param cn_denorm control parameter to feature extractor * @return List of micro-features extracted from the blob. * @note Exceptions: none * @note History: 7/21/89, DSJ, Created. */ MICROFEATURES BlobMicroFeatures(TBLOB* Blob, const DENORM& cn_denorm) { MICROFEATURES MicroFeatures = NIL_LIST; LIST Outlines; LIST RemainingOutlines; MFOUTLINE Outline; if (Blob != NULL) { Outlines = ConvertBlob(Blob); RemainingOutlines = Outlines; iterate(RemainingOutlines) { Outline = (MFOUTLINE) first_node (RemainingOutlines); CharNormalizeOutline(Outline, cn_denorm); } RemainingOutlines = Outlines; iterate(RemainingOutlines) { Outline = (MFOUTLINE) first_node(RemainingOutlines); FindDirectionChanges(Outline, classify_min_slope, classify_max_slope); MarkDirectionChanges(Outline); MicroFeatures = ConvertToMicroFeatures(Outline, MicroFeatures); } FreeOutlines(Outlines); } return MicroFeatures; } /* BlobMicroFeatures */ /*--------------------------------------------------------------------------- Private Code ---------------------------------------------------------------------------*/ /** * This routine computes the orientation parameter of the * specified micro-feature. The orientation is the angle of * the vector from Start to End. It is normalized to a number * between 0 and 1 where 0 corresponds to 0 degrees and 1 * corresponds to 360 degrees. The actual range is [0,1), i.e. * 1 is excluded from the range (since it is actual the * same orientation as 0). This routine assumes that Start * and End are not the same point. * @param Start starting edge point of micro-feature * @param End ending edge point of micro-feature * @note Globals: none * @return Orientation parameter for the specified micro-feature. * @note Exceptions: none * @note History: 7/27/89, DSJ, Created. */ FLOAT32 ComputeOrientation(MFEDGEPT *Start, MFEDGEPT *End) { FLOAT32 Orientation; Orientation = NormalizeAngle (AngleFrom (Start->Point, End->Point)); /* ensure that round-off errors do not put circular param out of range */ if ((Orientation < 0) || (Orientation >= 1)) Orientation = 0; return (Orientation); } /* ComputeOrientation */ /** * Convert Outline to MicroFeatures * @param Outline outline to extract micro-features from * @param MicroFeatures list of micro-features to add to * @return List of micro-features with new features added to front. * @note Globals: none * @note Exceptions: none * @note History: 7/26/89, DSJ, Created. */ MICROFEATURES ConvertToMicroFeatures(MFOUTLINE Outline, MICROFEATURES MicroFeatures) { MFOUTLINE Current; MFOUTLINE Last; MFOUTLINE First; MICROFEATURE NewFeature; if (DegenerateOutline (Outline)) return (MicroFeatures); First = NextExtremity (Outline); Last = First; do { Current = NextExtremity (Last); if (!PointAt(Current)->Hidden) { NewFeature = ExtractMicroFeature (Last, Current); if (NewFeature != NULL) MicroFeatures = push (MicroFeatures, NewFeature); } Last = Current; } while (Last != First); return (MicroFeatures); } /* ConvertToMicroFeatures */ /** * This routine computes the feature parameters which describe * the micro-feature that starts and Start and ends at End. * A new micro-feature is allocated, filled with the feature * parameters, and returned. The routine assumes that * Start and End are not the same point. If they are the * same point, NULL is returned, a warning message is * printed, and the current outline is dumped to stdout. * @param Start starting point of micro-feature * @param End ending point of micro-feature * @return New micro-feature or NULL if the feature was rejected. * @note Globals: none * @note Exceptions: none * @note History: * - 7/26/89, DSJ, Created. * - 11/17/89, DSJ, Added handling for Start and End same point. */ MICROFEATURE ExtractMicroFeature(MFOUTLINE Start, MFOUTLINE End) { MICROFEATURE NewFeature; MFEDGEPT *P1, *P2; P1 = PointAt(Start); P2 = PointAt(End); NewFeature = NewMicroFeature (); NewFeature[XPOSITION] = AverageOf(P1->Point.x, P2->Point.x); NewFeature[YPOSITION] = AverageOf(P1->Point.y, P2->Point.y); NewFeature[MFLENGTH] = DistanceBetween(P1->Point, P2->Point); NewFeature[ORIENTATION] = NormalizedAngleFrom(&P1->Point, &P2->Point, 1.0); NewFeature[FIRSTBULGE] = 0.0f; // deprecated NewFeature[SECONDBULGE] = 0.0f; // deprecated return NewFeature; } /* ExtractMicroFeature */ tesseract-3.04.01/classify/mfx.h000066400000000000000000000036331266071204500164440ustar00rootroot00000000000000/****************************************************************************** ** Filename: mfx.h ** Purpose: Definition of micro-feature extraction routines ** Author: Dan Johnson ** History: 5/29/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 MFX_H #define MFX_H /*---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "mfdefs.h" #include "params.h" /*---------------------------------------------------------------------------- Variables ----------------------------------------------------------------------------**/ /* old numbers corresponded to 10.0 degrees and 80.0 degrees */ extern double_VAR_H(classify_min_slope, 0.414213562, "Slope below which lines are called horizontal"); extern double_VAR_H(classify_max_slope, 2.414213562, "Slope above which lines are called vertical"); /*---------------------------------------------------------------------------- Public Function Prototypes ----------------------------------------------------------------------------**/ MICROFEATURES BlobMicroFeatures(TBLOB* Blob, const DENORM& cn_denorm); #endif tesseract-3.04.01/classify/normfeat.cpp000066400000000000000000000060131266071204500200130ustar00rootroot00000000000000/****************************************************************************** ** Filename: normfeat.c ** Purpose: Definition of char normalization features. ** Author: Dan Johnson ** History: 12/14/90, 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 "normfeat.h" #include "intfx.h" #include "featdefs.h" #include "mfoutline.h" /*---------------------------------------------------------------------------- Public Code ----------------------------------------------------------------------------*/ /** Return the length of the outline in baseline normalized form. */ FLOAT32 ActualOutlineLength(FEATURE Feature) { return (Feature->Params[CharNormLength] * LENGTH_COMPRESSION); } /** * Return the character normalization feature for a blob. * * The features returned are in a scale where the x-height has been * normalized to live in the region y = [-0.25 .. 0.25]. Example ranges * for English below are based on the Linux font collection on 2009-12-04: * * - Params[CharNormY] * - The y coordinate of the grapheme's centroid. * - English: [-0.27, 0.71] * * - Params[CharNormLength] * - The length of the grapheme's outline (tiny segments discarded), * divided by 10.0=LENGTH_COMPRESSION. * - English: [0.16, 0.85] * * - Params[CharNormRx] * - The radius of gyration about the x axis, as measured from CharNormY. * - English: [0.011, 0.34] * * - Params[CharNormRy] * - The radius of gyration about the y axis, as measured from * the x center of the grapheme's bounding box. * - English: [0.011, 0.31] */ FEATURE_SET ExtractCharNormFeatures(const INT_FX_RESULT_STRUCT& fx_info) { FEATURE_SET feature_set = NewFeatureSet(1); FEATURE feature = NewFeature(&CharNormDesc); feature->Params[CharNormY] = MF_SCALE_FACTOR * (fx_info.Ymean - kBlnBaselineOffset); feature->Params[CharNormLength] = MF_SCALE_FACTOR * fx_info.Length / LENGTH_COMPRESSION; feature->Params[CharNormRx] = MF_SCALE_FACTOR * fx_info.Rx; feature->Params[CharNormRy] = MF_SCALE_FACTOR * fx_info.Ry; AddFeature(feature_set, feature); return feature_set; } /* ExtractCharNormFeatures */ tesseract-3.04.01/classify/normfeat.h000066400000000000000000000031621266071204500174620ustar00rootroot00000000000000/****************************************************************************** ** Filename: normfeat.h ** Purpose: Definition of character normalization features. ** Author: Dan Johnson ** History: 12/14/90, 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 NORMFEAT_H #define NORMFEAT_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "ocrfeatures.h" #define LENGTH_COMPRESSION (10.0) typedef enum { CharNormY, CharNormLength, CharNormRx, CharNormRy } NORM_PARAM_NAME; /**---------------------------------------------------------------------------- Public Function Prototypes ----------------------------------------------------------------------------**/ FLOAT32 ActualOutlineLength(FEATURE Feature); FEATURE_SET ExtractCharNormFeatures(const INT_FX_RESULT_STRUCT& fx_info); #endif tesseract-3.04.01/classify/normmatch.cpp000066400000000000000000000234551266071204500202010ustar00rootroot00000000000000/****************************************************************************** ** Filename: normmatch.c ** Purpose: Simple matcher based on character normalization features. ** Author: Dan Johnson ** History: Wed Dec 19 16:18:06 1990, 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 "normmatch.h" #include #include #include "classify.h" #include "clusttool.h" #include "const.h" #include "efio.h" #include "emalloc.h" #include "globals.h" #include "helpers.h" #include "normfeat.h" #include "scanutils.h" #include "unicharset.h" #include "params.h" struct NORM_PROTOS { int NumParams; PARAM_DESC *ParamDesc; LIST* Protos; int NumProtos; }; /*---------------------------------------------------------------------------- Private Function Prototypes ----------------------------------------------------------------------------*/ double NormEvidenceOf(register double NormAdj); void PrintNormMatch(FILE *File, int NumParams, PROTOTYPE *Proto, FEATURE Feature); NORM_PROTOS *ReadNormProtos(FILE *File); /*---------------------------------------------------------------------------- Variables ----------------------------------------------------------------------------*/ /** control knobs used to control the normalization adjustment process */ double_VAR(classify_norm_adj_midpoint, 32.0, "Norm adjust midpoint ..."); double_VAR(classify_norm_adj_curl, 2.0, "Norm adjust curl ..."); /** Weight of width variance against height and vertical position. */ const double kWidthErrorWeighting = 0.125; /*---------------------------------------------------------------------------- Public Code ----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ namespace tesseract { /** * This routine compares Features against each character * normalization proto for ClassId and returns the match * rating of the best match. * @param ClassId id of class to match against * @param feature character normalization feature * @param DebugMatch controls dump of debug info * * Globals: * #NormProtos character normalization prototypes * * @return Best match rating for Feature against protos of ClassId. * @note Exceptions: none * @note History: Wed Dec 19 16:56:12 1990, DSJ, Created. */ FLOAT32 Classify::ComputeNormMatch(CLASS_ID ClassId, const FEATURE_STRUCT& feature, BOOL8 DebugMatch) { LIST Protos; FLOAT32 BestMatch; FLOAT32 Match; FLOAT32 Delta; PROTOTYPE *Proto; int ProtoId; if (ClassId >= NormProtos->NumProtos) { ClassId = NO_CLASS; } /* handle requests for classification as noise */ if (ClassId == NO_CLASS) { /* kludge - clean up constants and make into control knobs later */ Match = (feature.Params[CharNormLength] * feature.Params[CharNormLength] * 500.0 + feature.Params[CharNormRx] * feature.Params[CharNormRx] * 8000.0 + feature.Params[CharNormRy] * feature.Params[CharNormRy] * 8000.0); return (1.0 - NormEvidenceOf (Match)); } BestMatch = MAX_FLOAT32; Protos = NormProtos->Protos[ClassId]; if (DebugMatch) { tprintf("\nChar norm for class %s\n", unicharset.id_to_unichar(ClassId)); } ProtoId = 0; iterate(Protos) { Proto = (PROTOTYPE *) first_node (Protos); Delta = feature.Params[CharNormY] - Proto->Mean[CharNormY]; Match = Delta * Delta * Proto->Weight.Elliptical[CharNormY]; if (DebugMatch) { tprintf("YMiddle: Proto=%g, Delta=%g, Var=%g, Dist=%g\n", Proto->Mean[CharNormY], Delta, Proto->Weight.Elliptical[CharNormY], Match); } Delta = feature.Params[CharNormRx] - Proto->Mean[CharNormRx]; Match += Delta * Delta * Proto->Weight.Elliptical[CharNormRx]; if (DebugMatch) { tprintf("Height: Proto=%g, Delta=%g, Var=%g, Dist=%g\n", Proto->Mean[CharNormRx], Delta, Proto->Weight.Elliptical[CharNormRx], Match); } // Ry is width! See intfx.cpp. Delta = feature.Params[CharNormRy] - Proto->Mean[CharNormRy]; if (DebugMatch) { tprintf("Width: Proto=%g, Delta=%g, Var=%g\n", Proto->Mean[CharNormRy], Delta, Proto->Weight.Elliptical[CharNormRy]); } Delta = Delta * Delta * Proto->Weight.Elliptical[CharNormRy]; Delta *= kWidthErrorWeighting; Match += Delta; if (DebugMatch) { tprintf("Total Dist=%g, scaled=%g, sigmoid=%g, penalty=%g\n", Match, Match / classify_norm_adj_midpoint, NormEvidenceOf(Match), 256 * (1 - NormEvidenceOf(Match))); } if (Match < BestMatch) BestMatch = Match; ProtoId++; } return 1.0 - NormEvidenceOf(BestMatch); } /* ComputeNormMatch */ void Classify::FreeNormProtos() { if (NormProtos != NULL) { for (int i = 0; i < NormProtos->NumProtos; i++) FreeProtoList(&NormProtos->Protos[i]); Efree(NormProtos->Protos); Efree(NormProtos->ParamDesc); Efree(NormProtos); NormProtos = NULL; } } } // namespace tesseract /*---------------------------------------------------------------------------- Private Code ----------------------------------------------------------------------------*/ /** * @name NormEvidenceOf * * Return the new type of evidence number corresponding to this * normalization adjustment. The equation that represents the transform is: * 1 / (1 + (NormAdj / midpoint) ^ curl) */ double NormEvidenceOf(register double NormAdj) { NormAdj /= classify_norm_adj_midpoint; if (classify_norm_adj_curl == 3) NormAdj = NormAdj * NormAdj * NormAdj; else if (classify_norm_adj_curl == 2) NormAdj = NormAdj * NormAdj; else NormAdj = pow (NormAdj, classify_norm_adj_curl); return (1.0 / (1.0 + NormAdj)); } /*---------------------------------------------------------------------------*/ /** * This routine dumps out detailed normalization match info. * @param File open text file to dump match debug info to * @param NumParams # of parameters in proto and feature * @param Proto[] array of prototype parameters * @param Feature[] array of feature parameters * Globals: none * @return none * @note Exceptions: none * @note History: Wed Jan 2 09:49:35 1991, DSJ, Created. */ void PrintNormMatch(FILE *File, int NumParams, PROTOTYPE *Proto, FEATURE Feature) { int i; FLOAT32 ParamMatch; FLOAT32 TotalMatch; for (i = 0, TotalMatch = 0.0; i < NumParams; i++) { ParamMatch = (Feature->Params[i] - Mean(Proto, i)) / StandardDeviation(Proto, i); fprintf (File, " %6.1f", ParamMatch); if (i == CharNormY || i == CharNormRx) TotalMatch += ParamMatch * ParamMatch; } fprintf (File, " --> %6.1f (%4.2f)\n", TotalMatch, NormEvidenceOf (TotalMatch)); } /* PrintNormMatch */ /*---------------------------------------------------------------------------*/ namespace tesseract { /** * This routine allocates a new data structure to hold * a set of character normalization protos. It then fills in * the data structure by reading from the specified File. * @param File open text file to read normalization protos from * @param end_offset * Globals: none * @return Character normalization protos. * @note Exceptions: none * @note History: Wed Dec 19 16:38:49 1990, DSJ, Created. */ NORM_PROTOS *Classify::ReadNormProtos(FILE *File, inT64 end_offset) { NORM_PROTOS *NormProtos; int i; char unichar[2 * UNICHAR_LEN + 1]; UNICHAR_ID unichar_id; LIST Protos; int NumProtos; /* allocate and initialization data structure */ NormProtos = (NORM_PROTOS *) Emalloc (sizeof (NORM_PROTOS)); NormProtos->NumProtos = unicharset.size(); NormProtos->Protos = (LIST *) Emalloc (NormProtos->NumProtos * sizeof(LIST)); for (i = 0; i < NormProtos->NumProtos; i++) NormProtos->Protos[i] = NIL_LIST; /* read file header and save in data structure */ NormProtos->NumParams = ReadSampleSize (File); NormProtos->ParamDesc = ReadParamDesc (File, NormProtos->NumParams); /* read protos for each class into a separate list */ while ((end_offset < 0 || ftell(File) < end_offset) && tfscanf(File, "%s %d", unichar, &NumProtos) == 2) { if (unicharset.contains_unichar(unichar)) { unichar_id = unicharset.unichar_to_id(unichar); Protos = NormProtos->Protos[unichar_id]; for (i = 0; i < NumProtos; i++) Protos = push_last (Protos, ReadPrototype (File, NormProtos->NumParams)); NormProtos->Protos[unichar_id] = Protos; } else { cprintf("Error: unichar %s in normproto file is not in unichar set.\n", unichar); for (i = 0; i < NumProtos; i++) FreePrototype(ReadPrototype (File, NormProtos->NumParams)); } SkipNewline(File); } return (NormProtos); } /* ReadNormProtos */ } // namespace tesseract tesseract-3.04.01/classify/normmatch.h000066400000000000000000000032521266071204500176370ustar00rootroot00000000000000/****************************************************************************** ** Filename: normmatch.h ** Purpose: Simple matcher based on character normalization features. ** Author: Dan Johnson ** History: Thu Dec 20 08:55:05 1990, 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 NORMMATCH_H #define NORMMATCH_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "matchdefs.h" #include "ocrfeatures.h" #include "params.h" /**---------------------------------------------------------------------------- Variables ----------------------------------------------------------------------------**/ /* control knobs used to control the normalization adjustment process */ extern double_VAR_H(classify_norm_adj_midpoint, 32.0, "Norm adjust midpoint ..."); extern double_VAR_H(classify_norm_adj_curl, 2.0, "Norm adjust curl ..."); #endif tesseract-3.04.01/classify/ocrfeatures.cpp000066400000000000000000000216321266071204500205260ustar00rootroot00000000000000/****************************************************************************** ** Filename: features.c ** Purpose: Generic definition of a feature. ** Author: Dan Johnson ** History: Mon May 21 10:49:04 1990, 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 "ocrfeatures.h" #include "emalloc.h" #include "callcpp.h" #include "danerror.h" #include "freelist.h" #include "scanutils.h" #include #include /*---------------------------------------------------------------------------- Public Code ----------------------------------------------------------------------------*/ /** * Add a feature to a feature set. If the feature set is * already full, FALSE is returned to indicate that the * feature could not be added to the set; otherwise, TRUE is * returned. * @param FeatureSet set of features to add Feature to * @param Feature feature to be added to FeatureSet * @return TRUE if feature added to set, FALSE if set is already full. * @note History: Tue May 22 17:22:23 1990, DSJ, Created. */ BOOL8 AddFeature(FEATURE_SET FeatureSet, FEATURE Feature) { if (FeatureSet->NumFeatures >= FeatureSet->MaxNumFeatures) { FreeFeature(Feature); return FALSE; } FeatureSet->Features[FeatureSet->NumFeatures++] = Feature; return TRUE; } /* AddFeature */ /** * Release the memory consumed by the specified feature. * @param Feature feature to be deallocated. * @return none * @note History: Mon May 21 13:33:27 1990, DSJ, Created. */ void FreeFeature(FEATURE Feature) { if (Feature) { free_struct (Feature, sizeof (FEATURE_STRUCT) + sizeof (FLOAT32) * (Feature->Type->NumParams - 1), "sizeof(FEATURE_STRUCT)+sizeof(FLOAT32)*(NumParamsIn(Feature)-1)"); } } /* FreeFeature */ /** * Release the memory consumed by the specified feature * set. This routine also frees the memory consumed by the * features contained in the set. * @param FeatureSet set of features to be freed * @return none * @note History: Mon May 21 13:59:46 1990, DSJ, Created. */ void FreeFeatureSet(FEATURE_SET FeatureSet) { int i; if (FeatureSet) { for (i = 0; i < FeatureSet->NumFeatures; i++) FreeFeature(FeatureSet->Features[i]); memfree(FeatureSet); } } /* FreeFeatureSet */ /** * Allocate and return a new feature of the specified * type. * @param FeatureDesc description of feature to be created. * @return New #FEATURE. * @note History: Mon May 21 14:06:42 1990, DSJ, Created. */ FEATURE NewFeature(const FEATURE_DESC_STRUCT* FeatureDesc) { FEATURE Feature; Feature = (FEATURE) alloc_struct (sizeof (FEATURE_STRUCT) + (FeatureDesc->NumParams - 1) * sizeof (FLOAT32), "sizeof(FEATURE_STRUCT)+sizeof(FLOAT32)*(NumParamsIn(Feature)-1)"); Feature->Type = FeatureDesc; return (Feature); } /* NewFeature */ /** * Allocate and return a new feature set large enough to * hold the specified number of features. * @param NumFeatures maximum # of features to be put in feature set * @return New #FEATURE_SET. * @note History: Mon May 21 14:22:40 1990, DSJ, Created. */ FEATURE_SET NewFeatureSet(int NumFeatures) { FEATURE_SET FeatureSet; FeatureSet = (FEATURE_SET) Emalloc (sizeof (FEATURE_SET_STRUCT) + (NumFeatures - 1) * sizeof (FEATURE)); FeatureSet->MaxNumFeatures = NumFeatures; FeatureSet->NumFeatures = 0; return (FeatureSet); } /* NewFeatureSet */ /** * Create a new feature of the specified type and read in * the value of its parameters from File. The extra penalty * for the feature is also computed by calling the appropriate * function for the specified feature type. The correct text * representation for a feature is a list of N floats where * N is the number of parameters in the feature. * @param File open text file to read feature from * @param FeatureDesc specifies type of feature to read from File * @return New #FEATURE read from File. * @note Exceptions: #ILLEGAL_FEATURE_PARAM if text file doesn't match expected format * @note History: Wed May 23 08:53:16 1990, DSJ, Created. */ FEATURE ReadFeature(FILE *File, const FEATURE_DESC_STRUCT* FeatureDesc) { FEATURE Feature; int i; Feature = NewFeature (FeatureDesc); for (i = 0; i < Feature->Type->NumParams; i++) { if (tfscanf(File, "%f", &(Feature->Params[i])) != 1) DoError (ILLEGAL_FEATURE_PARAM, "Illegal feature parameter spec"); #ifndef _WIN32 assert (!isnan(Feature->Params[i])); #endif } return (Feature); } /* ReadFeature */ /** * Create a new feature set of the specified type and read in * the features from File. The correct text representation * for a feature set is an integer which specifies the number (N) * of features in a set followed by a list of N feature * descriptions. * @param File open text file to read new feature set from * @param FeatureDesc specifies type of feature to read from File * @return New feature set read from File. * @note History: Wed May 23 09:17:31 1990, DSJ, Created. */ FEATURE_SET ReadFeatureSet(FILE *File, const FEATURE_DESC_STRUCT* FeatureDesc) { FEATURE_SET FeatureSet; int NumFeatures; int i; if (tfscanf(File, "%d", &NumFeatures) != 1 || NumFeatures < 0) DoError(ILLEGAL_NUM_FEATURES, "Illegal number of features in set"); FeatureSet = NewFeatureSet(NumFeatures); for (i = 0; i < NumFeatures; i++) AddFeature(FeatureSet, ReadFeature (File, FeatureDesc)); return (FeatureSet); } /* ReadFeatureSet */ /** * Appends a textual representation of Feature to str. * This representation is simply a list of the N parameters * of the feature, terminated with a newline. It is assumed * that the ExtraPenalty field can be reconstructed from the * parameters of the feature. It is also assumed that the * feature type information is specified or assumed elsewhere. * @param Feature feature to write out to str * @param str string to write Feature to * @return none * @note History: Wed May 23 09:28:18 1990, DSJ, Created. */ void WriteFeature(FEATURE Feature, STRING* str) { for (int i = 0; i < Feature->Type->NumParams; i++) { #ifndef WIN32 assert(!isnan(Feature->Params[i])); #endif str->add_str_double(" ", Feature->Params[i]); } *str += "\n"; } /* WriteFeature */ /** * Write a textual representation of FeatureSet to File. * This representation is an integer specifying the number of * features in the set, followed by a newline, followed by * text representations for each feature in the set. * @param FeatureSet feature set to write to File * @param str string to write Feature to * @return none * @note History: Wed May 23 10:06:03 1990, DSJ, Created. */ void WriteFeatureSet(FEATURE_SET FeatureSet, STRING* str) { if (FeatureSet) { str->add_str_int("", FeatureSet->NumFeatures); *str += "\n"; for (int i = 0; i < FeatureSet->NumFeatures; i++) { WriteFeature(FeatureSet->Features[i], str); } } } /* WriteFeatureSet */ /** * Write a textual representation of FeatureDesc to File * in the old format (i.e. the format used by the clusterer). * * This format is: * @verbatim * Number of Params * Description of Param 1 * ... * @endverbatim * @param File open text file to write FeatureDesc to * @param FeatureDesc feature descriptor to write to File * @return none * @note History: Fri May 25 15:27:18 1990, DSJ, Created. */ void WriteOldParamDesc(FILE *File, const FEATURE_DESC_STRUCT* FeatureDesc) { int i; fprintf (File, "%d\n", FeatureDesc->NumParams); for (i = 0; i < FeatureDesc->NumParams; i++) { if (FeatureDesc->ParamDesc[i].Circular) fprintf (File, "circular "); else fprintf (File, "linear "); if (FeatureDesc->ParamDesc[i].NonEssential) fprintf (File, "non-essential "); else fprintf (File, "essential "); fprintf (File, "%f %f\n", FeatureDesc->ParamDesc[i].Min, FeatureDesc->ParamDesc[i].Max); } } /* WriteOldParamDesc */ tesseract-3.04.01/classify/ocrfeatures.h000066400000000000000000000116011266071204500201660ustar00rootroot00000000000000/****************************************************************************** ** Filename: features.h ** Purpose: Generic definition of a feature. ** Author: Dan Johnson ** History: Sun May 20 10:28:30 1990, 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 FEATURES_H #define FEATURES_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "blobs.h" #include class DENORM; struct INT_FX_RESULT_STRUCT; #undef Min #undef Max #define FEAT_NAME_SIZE 80 // define trap errors which can be caused by this module #define ILLEGAL_FEATURE_PARAM 1000 #define ILLEGAL_NUM_FEATURES 1001 // A character is described by multiple sets of extracted features. Each // set contains a number of features of a particular type, for example, a // set of bays, or a set of closures, or a set of microfeatures. Each // feature consists of a number of parameters. All features within a // feature set contain the same number of parameters. All circular // parameters are required to be the first parameters in the feature. struct PARAM_DESC { inT8 Circular; // TRUE if dimension wraps around inT8 NonEssential; // TRUE if dimension not used in searches FLOAT32 Min; // low end of range for circular dimensions FLOAT32 Max; // high end of range for circular dimensions FLOAT32 Range; // Max - Min FLOAT32 HalfRange; // (Max - Min)/2 FLOAT32 MidRange; // (Max + Min)/2 }; struct FEATURE_DESC_STRUCT { uinT16 NumParams; // total # of params const char *ShortName; // short name for feature const PARAM_DESC *ParamDesc; // array - one per param }; typedef FEATURE_DESC_STRUCT *FEATURE_DESC; struct FEATURE_STRUCT { const FEATURE_DESC_STRUCT *Type; // points to description of feature type FLOAT32 Params[1]; // variable size array - params for feature }; typedef FEATURE_STRUCT *FEATURE; struct FEATURE_SET_STRUCT { uinT16 NumFeatures; // number of features in set uinT16 MaxNumFeatures; // maximum size of feature set FEATURE Features[1]; // variable size array of features }; typedef FEATURE_SET_STRUCT *FEATURE_SET; // A generic character description as a char pointer. In reality, it will be // a pointer to some data structure. Paired feature extractors/matchers need // to agree on the data structure to be used, however, the high level // classifier does not need to know the details of this data structure. typedef char *CHAR_FEATURES; /*---------------------------------------------------------------------- Macros for defining the parameters of a new features ----------------------------------------------------------------------*/ #define StartParamDesc(Name) \ const PARAM_DESC Name[] = { #define DefineParam(Circular, NonEssential, Min, Max) \ {Circular, NonEssential, Min, Max, \ (Max) - (Min), (((Max) - (Min))/2.0), (((Max) + (Min))/2.0)}, #define EndParamDesc }; /*---------------------------------------------------------------------- Macro for describing a new feature. The parameters of the macro are as follows: DefineFeature (Name, NumLinear, NumCircular, ShortName, ParamName) ----------------------------------------------------------------------*/ #define DefineFeature(Name, NL, NC, SN, PN) \ const FEATURE_DESC_STRUCT Name = { \ ((NL) + (NC)), SN, PN}; /*---------------------------------------------------------------------- Generic routines that work for all feature types ----------------------------------------------------------------------*/ BOOL8 AddFeature(FEATURE_SET FeatureSet, FEATURE Feature); void FreeFeature(FEATURE Feature); void FreeFeatureSet(FEATURE_SET FeatureSet); FEATURE NewFeature(const FEATURE_DESC_STRUCT *FeatureDesc); FEATURE_SET NewFeatureSet(int NumFeatures); FEATURE ReadFeature(FILE *File, const FEATURE_DESC_STRUCT *FeatureDesc); FEATURE_SET ReadFeatureSet(FILE *File, const FEATURE_DESC_STRUCT *FeatureDesc); void WriteFeature(FEATURE Feature, STRING* str); void WriteFeatureSet(FEATURE_SET FeatureSet, STRING* str); #endif tesseract-3.04.01/classify/outfeat.cpp000066400000000000000000000150751266071204500176570ustar00rootroot00000000000000/****************************************************************************** ** Filename: outfeat.c ** Purpose: Definition of outline-features. ** Author: Dan Johnson ** History: 11/13/90, 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 "outfeat.h" #include "classify.h" #include "efio.h" #include "featdefs.h" #include "mfoutline.h" #include "ocrfeatures.h" #include /*---------------------------------------------------------------------------- Public Code ----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ namespace tesseract { /** * Convert each segment in the outline to a feature * and return the features. * @param Blob blob to extract pico-features from * @return Outline-features for Blob. * @note Globals: none * @note Exceptions: none * @note History: * - 11/13/90, DSJ, Created. * - 05/24/91, DSJ, Updated for either char or baseline normalize. */ FEATURE_SET Classify::ExtractOutlineFeatures(TBLOB *Blob) { LIST Outlines; LIST RemainingOutlines; MFOUTLINE Outline; FEATURE_SET FeatureSet; FLOAT32 XScale, YScale; FeatureSet = NewFeatureSet (MAX_OUTLINE_FEATURES); if (Blob == NULL) return (FeatureSet); Outlines = ConvertBlob (Blob); NormalizeOutlines(Outlines, &XScale, &YScale); RemainingOutlines = Outlines; iterate(RemainingOutlines) { Outline = (MFOUTLINE) first_node (RemainingOutlines); ConvertToOutlineFeatures(Outline, FeatureSet); } if (classify_norm_method == baseline) NormalizeOutlineX(FeatureSet); FreeOutlines(Outlines); return (FeatureSet); } /* ExtractOutlineFeatures */ } // namespace tesseract /*---------------------------------------------------------------------------- Private Code ----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /** * This routine computes the midpoint between Start and * End to obtain the x,y position of the outline-feature. It * also computes the direction from Start to End as the * direction of the outline-feature and the distance from * Start to End as the length of the outline-feature. * This feature is then * inserted into the next feature slot in FeatureSet. * @param Start starting point of outline-feature * @param End ending point of outline-feature * @param FeatureSet set to add outline-feature to * @return none (results are placed in FeatureSet) * @note Globals: none * @note Exceptions: none * @note History: 11/13/90, DSJ, Created. */ void AddOutlineFeatureToSet(FPOINT *Start, FPOINT *End, FEATURE_SET FeatureSet) { FEATURE Feature; Feature = NewFeature(&OutlineFeatDesc); Feature->Params[OutlineFeatDir] = NormalizedAngleFrom(Start, End, 1.0); Feature->Params[OutlineFeatX] = AverageOf(Start->x, End->x); Feature->Params[OutlineFeatY] = AverageOf(Start->y, End->y); Feature->Params[OutlineFeatLength] = DistanceBetween(*Start, *End); AddFeature(FeatureSet, Feature); } /* AddOutlineFeatureToSet */ /*---------------------------------------------------------------------------*/ /** * This routine steps converts each section in the specified * outline to a feature described by its x,y position, length * and angle. * @param Outline outline to extract outline-features from * @param FeatureSet set of features to add outline-features to * @return none (results are returned in FeatureSet) * @note Globals: none * @note Exceptions: none * @note History: * - 11/13/90, DSJ, Created. * - 5/24/91, DSJ, Added hidden edge capability. */ void ConvertToOutlineFeatures(MFOUTLINE Outline, FEATURE_SET FeatureSet) { MFOUTLINE Next; MFOUTLINE First; FPOINT FeatureStart; FPOINT FeatureEnd; if (DegenerateOutline (Outline)) return; First = Outline; Next = First; do { FeatureStart = PointAt(Next)->Point; Next = NextPointAfter(Next); /* note that an edge is hidden if the ending point of the edge is marked as hidden. This situation happens because the order of the outlines is reversed when they are converted from the old format. In the old format, a hidden edge is marked by the starting point for that edge. */ if (!PointAt(Next)->Hidden) { FeatureEnd = PointAt(Next)->Point; AddOutlineFeatureToSet(&FeatureStart, &FeatureEnd, FeatureSet); } } while (Next != First); } /* ConvertToOutlineFeatures */ /*---------------------------------------------------------------------------*/ /** * This routine computes the weighted average x position * over all of the outline-features in FeatureSet and then * renormalizes the outline-features to force this average * to be the x origin (i.e. x=0). * @param FeatureSet outline-features to be normalized * @return none (FeatureSet is changed) * @note Globals: none * @note Exceptions: none * @note History: 11/13/90, DSJ, Created. */ void NormalizeOutlineX(FEATURE_SET FeatureSet) { int i; FEATURE Feature; FLOAT32 Length; FLOAT32 TotalX = 0.0; FLOAT32 TotalWeight = 0.0; FLOAT32 Origin; if (FeatureSet->NumFeatures <= 0) return; for (i = 0; i < FeatureSet->NumFeatures; i++) { Feature = FeatureSet->Features[i]; Length = Feature->Params[OutlineFeatLength]; TotalX += Feature->Params[OutlineFeatX] * Length; TotalWeight += Length; } Origin = TotalX / TotalWeight; for (i = 0; i < FeatureSet->NumFeatures; i++) { Feature = FeatureSet->Features[i]; Feature->Params[OutlineFeatX] -= Origin; } } /* NormalizeOutlineX */ tesseract-3.04.01/classify/outfeat.h000066400000000000000000000034561266071204500173240ustar00rootroot00000000000000/****************************************************************************** ** Filename: outfeat.h ** Purpose: Definition of outline features. ** Author: Dan Johnson ** History: 11/13/90, 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 OUTFEAT_H #define OUTFEAT_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "ocrfeatures.h" #include "fpoint.h" #include "mfoutline.h" typedef enum { OutlineFeatX, OutlineFeatY, OutlineFeatLength, OutlineFeatDir } OUTLINE_FEAT_PARAM_NAME; #define MAX_OUTLINE_FEATURES (100) /*--------------------------------------------------------------------------- Privat Function Prototypes ----------------------------------------------------------------------------*/ void AddOutlineFeatureToSet(FPOINT *Start, FPOINT *End, FEATURE_SET FeatureSet); void ConvertToOutlineFeatures(MFOUTLINE Outline, FEATURE_SET FeatureSet); void NormalizeOutlineX(FEATURE_SET FeatureSet); #endif tesseract-3.04.01/classify/picofeat.cpp000066400000000000000000000236171266071204500200030ustar00rootroot00000000000000/****************************************************************************** ** Filename: picofeat.c ** Purpose: Definition of pico-features. ** Author: Dan Johnson ** History: 9/4/90, 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 "picofeat.h" #include "classify.h" #include "efio.h" #include "featdefs.h" #include "fpoint.h" #include "mfoutline.h" #include "ocrfeatures.h" #include "params.h" #include "trainingsample.h" #include #include /*--------------------------------------------------------------------------- Variables ----------------------------------------------------------------------------*/ double_VAR(classify_pico_feature_length, 0.05, "Pico Feature Length"); /*--------------------------------------------------------------------------- Private Function Prototypes ----------------------------------------------------------------------------*/ void ConvertSegmentToPicoFeat(FPOINT *Start, FPOINT *End, FEATURE_SET FeatureSet); void ConvertToPicoFeatures2(MFOUTLINE Outline, FEATURE_SET FeatureSet); void NormalizePicoX(FEATURE_SET FeatureSet); /*---------------------------------------------------------------------------- Public Code ----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ namespace tesseract { /** * Operation: Dummy for now. * * Globals: * - classify_norm_method normalization method currently specified * @param Blob blob to extract pico-features from * @return Pico-features for Blob. * @note Exceptions: none * @note History: 9/4/90, DSJ, Created. */ FEATURE_SET Classify::ExtractPicoFeatures(TBLOB *Blob) { LIST Outlines; LIST RemainingOutlines; MFOUTLINE Outline; FEATURE_SET FeatureSet; FLOAT32 XScale, YScale; FeatureSet = NewFeatureSet(MAX_PICO_FEATURES); Outlines = ConvertBlob(Blob); NormalizeOutlines(Outlines, &XScale, &YScale); RemainingOutlines = Outlines; iterate(RemainingOutlines) { Outline = (MFOUTLINE) first_node (RemainingOutlines); ConvertToPicoFeatures2(Outline, FeatureSet); } if (classify_norm_method == baseline) NormalizePicoX(FeatureSet); FreeOutlines(Outlines); return (FeatureSet); } /* ExtractPicoFeatures */ } // namespace tesseract /*---------------------------------------------------------------------------- Private Code ----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /** * This routine converts an entire segment of an outline * into a set of pico features which are added to * FeatureSet. The length of the segment is rounded to the * nearest whole number of pico-features. The pico-features * are spaced evenly over the entire segment. * Globals: * - classify_pico_feature_length length of a single pico-feature * @param Start starting point of pico-feature * @param End ending point of pico-feature * @param FeatureSet set to add pico-feature to * @return none (results are placed in FeatureSet) * @note Exceptions: none * @note History: Tue Apr 30 15:44:34 1991, DSJ, Created. */ void ConvertSegmentToPicoFeat(FPOINT *Start, FPOINT *End, FEATURE_SET FeatureSet) { FEATURE Feature; FLOAT32 Angle; FLOAT32 Length; int NumFeatures; FPOINT Center; FPOINT Delta; int i; Angle = NormalizedAngleFrom (Start, End, 1.0); Length = DistanceBetween (*Start, *End); NumFeatures = (int) floor (Length / classify_pico_feature_length + 0.5); if (NumFeatures < 1) NumFeatures = 1; /* compute vector for one pico feature */ Delta.x = XDelta (*Start, *End) / NumFeatures; Delta.y = YDelta (*Start, *End) / NumFeatures; /* compute position of first pico feature */ Center.x = Start->x + Delta.x / 2.0; Center.y = Start->y + Delta.y / 2.0; /* compute each pico feature in segment and add to feature set */ for (i = 0; i < NumFeatures; i++) { Feature = NewFeature (&PicoFeatDesc); Feature->Params[PicoFeatDir] = Angle; Feature->Params[PicoFeatX] = Center.x; Feature->Params[PicoFeatY] = Center.y; AddFeature(FeatureSet, Feature); Center.x += Delta.x; Center.y += Delta.y; } } /* ConvertSegmentToPicoFeat */ /*---------------------------------------------------------------------------*/ /** * This routine steps through the specified outline and cuts it * up into pieces of equal length. These pieces become the * desired pico-features. Each segment in the outline * is converted into an integral number of pico-features. * * Globals: * - classify_pico_feature_length length of features to be extracted * @param Outline outline to extract micro-features from * @param FeatureSet set of features to add pico-features to * @return none (results are returned in FeatureSet) * @note Exceptions: none * @note History: 4/30/91, DSJ, Adapted from ConvertToPicoFeatures(). */ void ConvertToPicoFeatures2(MFOUTLINE Outline, FEATURE_SET FeatureSet) { MFOUTLINE Next; MFOUTLINE First; MFOUTLINE Current; if (DegenerateOutline(Outline)) return; First = Outline; Current = First; Next = NextPointAfter(Current); do { /* note that an edge is hidden if the ending point of the edge is marked as hidden. This situation happens because the order of the outlines is reversed when they are converted from the old format. In the old format, a hidden edge is marked by the starting point for that edge. */ if (!(PointAt(Next)->Hidden)) ConvertSegmentToPicoFeat (&(PointAt(Current)->Point), &(PointAt(Next)->Point), FeatureSet); Current = Next; Next = NextPointAfter(Current); } while (Current != First); } /* ConvertToPicoFeatures2 */ /*---------------------------------------------------------------------------*/ /** * This routine computes the average x position over all * of the pico-features in FeatureSet and then renormalizes * the pico-features to force this average to be the x origin * (i.e. x=0). * @param FeatureSet pico-features to be normalized * @return none (FeatureSet is changed) * @note Globals: none * @note Exceptions: none * @note History: Tue Sep 4 16:50:08 1990, DSJ, Created. */ void NormalizePicoX(FEATURE_SET FeatureSet) { int i; FEATURE Feature; FLOAT32 Origin = 0.0; for (i = 0; i < FeatureSet->NumFeatures; i++) { Feature = FeatureSet->Features[i]; Origin += Feature->Params[PicoFeatX]; } Origin /= FeatureSet->NumFeatures; for (i = 0; i < FeatureSet->NumFeatures; i++) { Feature = FeatureSet->Features[i]; Feature->Params[PicoFeatX] -= Origin; } } /* NormalizePicoX */ namespace tesseract { /*---------------------------------------------------------------------------*/ /** * @param blob blob to extract features from * @param fx_info * @return Integer character-normalized features for blob. * @note Exceptions: none * @note History: 8/8/2011, rays, Created. */ FEATURE_SET Classify::ExtractIntCNFeatures( const TBLOB& blob, const INT_FX_RESULT_STRUCT& fx_info) { INT_FX_RESULT_STRUCT local_fx_info(fx_info); GenericVector bl_features; tesseract::TrainingSample* sample = tesseract::BlobToTrainingSample( blob, false, &local_fx_info, &bl_features); if (sample == NULL) return NULL; int num_features = sample->num_features(); const INT_FEATURE_STRUCT* features = sample->features(); FEATURE_SET feature_set = NewFeatureSet(num_features); for (int f = 0; f < num_features; ++f) { FEATURE feature = NewFeature(&IntFeatDesc); feature->Params[IntX] = features[f].X; feature->Params[IntY] = features[f].Y; feature->Params[IntDir] = features[f].Theta; AddFeature(feature_set, feature); } delete sample; return feature_set; } /* ExtractIntCNFeatures */ /*---------------------------------------------------------------------------*/ /** * @param blob blob to extract features from * @param fx_info * @return Geometric (top/bottom/width) features for blob. * @note Exceptions: none * @note History: 8/8/2011, rays, Created. */ FEATURE_SET Classify::ExtractIntGeoFeatures( const TBLOB& blob, const INT_FX_RESULT_STRUCT& fx_info) { INT_FX_RESULT_STRUCT local_fx_info(fx_info); GenericVector bl_features; tesseract::TrainingSample* sample = tesseract::BlobToTrainingSample( blob, false, &local_fx_info, &bl_features); if (sample == NULL) return NULL; FEATURE_SET feature_set = NewFeatureSet(1); FEATURE feature = NewFeature(&IntFeatDesc); feature->Params[GeoBottom] = sample->geo_feature(GeoBottom); feature->Params[GeoTop] = sample->geo_feature(GeoTop); feature->Params[GeoWidth] = sample->geo_feature(GeoWidth); AddFeature(feature_set, feature); delete sample; return feature_set; } /* ExtractIntGeoFeatures */ } // namespace tesseract. tesseract-3.04.01/classify/picofeat.h000066400000000000000000000047761266071204500174550ustar00rootroot00000000000000/****************************************************************************** ** Filename: picofeat.h ** Purpose: Definition of pico features. ** Author: Dan Johnson ** History: 9/4/90, 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 PICOFEAT_H #define PICOFEAT_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "ocrfeatures.h" #include "params.h" // Enum for the order/type of params in IntFeatDesc. enum IntParams { IntX, // x-position (0-255). IntY, // y-position (0-255). IntDir // Direction (0-255, circular). }; // Enum for the order/type of params in GeoFeatDesc. enum GeoParams { GeoBottom, // Bounding box bottom in baseline space (0-255). GeoTop, // Bounding box top in baseline space (0-255). GeoWidth, // Bounding box width in baseline space (0-255). GeoCount // Number of geo features. }; typedef enum { PicoFeatY, PicoFeatDir, PicoFeatX } PICO_FEAT_PARAM_NAME; #define MAX_PICO_FEATURES (1000) /*--------------------------------------------------------------------------- Variables ----------------------------------------------------------------------------*/ extern double_VAR_H(classify_pico_feature_length, 0.05, "Pico Feature Length"); /**---------------------------------------------------------------------------- Public Function Prototypes ----------------------------------------------------------------------------**/ #define GetPicoFeatureLength() (PicoFeatureLength) /**---------------------------------------------------------------------------- Global Data Definitions and Declarations ----------------------------------------------------------------------------**/ extern FLOAT32 PicoFeatureLength; #endif tesseract-3.04.01/classify/protos.cpp000066400000000000000000000173351266071204500175370ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: protos.c (Formerly protos.c) * Description: * Author: Mark Seaman, OCR Technology * Created: Fri Oct 16 14:37:00 1987 * Modified: Mon Mar 4 14:51:24 1991 (Dan Johnson) danj@hpgrlj * 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 "protos.h" #include "const.h" #include "emalloc.h" #include "freelist.h" #include "callcpp.h" #include "tprintf.h" #include "scanutils.h" #include "globals.h" #include "classify.h" #include "params.h" #include #include #define PROTO_INCREMENT 32 #define CONFIG_INCREMENT 16 /*---------------------------------------------------------------------- V a r i a b l e s ----------------------------------------------------------------------*/ CLASS_STRUCT TrainingData[NUMBER_OF_CLASSES]; STRING_VAR(classify_training_file, "MicroFeatures", "Training file"); /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ /** * @name AddConfigToClass * * Add a new config to this class. Malloc new space and copy the * old configs if necessary. Return the config id for the new config. * * @param Class The class to add to */ int AddConfigToClass(CLASS_TYPE Class) { int NewNumConfigs; int NewConfig; int MaxNumProtos; BIT_VECTOR Config; MaxNumProtos = Class->MaxNumProtos; if (Class->NumConfigs >= Class->MaxNumConfigs) { /* add configs in CONFIG_INCREMENT chunks at a time */ NewNumConfigs = (((Class->MaxNumConfigs + CONFIG_INCREMENT) / CONFIG_INCREMENT) * CONFIG_INCREMENT); Class->Configurations = (CONFIGS) Erealloc (Class->Configurations, sizeof (BIT_VECTOR) * NewNumConfigs); Class->MaxNumConfigs = NewNumConfigs; } NewConfig = Class->NumConfigs++; Config = NewBitVector (MaxNumProtos); Class->Configurations[NewConfig] = Config; zero_all_bits (Config, WordsInVectorOfSize (MaxNumProtos)); return (NewConfig); } /** * @name AddProtoToClass * * Add a new proto to this class. Malloc new space and copy the * old protos if necessary. Return the proto id for the new proto. * * @param Class The class to add to */ int AddProtoToClass(CLASS_TYPE Class) { int i; int Bit; int NewNumProtos; int NewProto; BIT_VECTOR Config; if (Class->NumProtos >= Class->MaxNumProtos) { /* add protos in PROTO_INCREMENT chunks at a time */ NewNumProtos = (((Class->MaxNumProtos + PROTO_INCREMENT) / PROTO_INCREMENT) * PROTO_INCREMENT); Class->Prototypes = (PROTO) Erealloc (Class->Prototypes, sizeof (PROTO_STRUCT) * NewNumProtos); Class->MaxNumProtos = NewNumProtos; for (i = 0; i < Class->NumConfigs; i++) { Config = Class->Configurations[i]; Class->Configurations[i] = ExpandBitVector (Config, NewNumProtos); for (Bit = Class->NumProtos; Bit < NewNumProtos; Bit++) reset_bit(Config, Bit); } } NewProto = Class->NumProtos++; if (Class->NumProtos > MAX_NUM_PROTOS) { tprintf("Ouch! number of protos = %d, vs max of %d!", Class->NumProtos, MAX_NUM_PROTOS); } return (NewProto); } /** * @name ClassConfigLength * * Return the length of all the protos in this class. * * @param Class The class to add to * @param Config FIXME */ FLOAT32 ClassConfigLength(CLASS_TYPE Class, BIT_VECTOR Config) { inT16 Pid; FLOAT32 TotalLength = 0; for (Pid = 0; Pid < Class->NumProtos; Pid++) { if (test_bit (Config, Pid)) { TotalLength += (ProtoIn (Class, Pid))->Length; } } return (TotalLength); } /** * @name ClassProtoLength * * Return the length of all the protos in this class. * * @param Class The class to use */ FLOAT32 ClassProtoLength(CLASS_TYPE Class) { inT16 Pid; FLOAT32 TotalLength = 0; for (Pid = 0; Pid < Class->NumProtos; Pid++) { TotalLength += (ProtoIn (Class, Pid))->Length; } return (TotalLength); } /** * @name CopyProto * * Copy the first proto into the second. * * @param Src Source * @param Dest Destination */ void CopyProto(PROTO Src, PROTO Dest) { Dest->X = Src->X; Dest->Y = Src->Y; Dest->Length = Src->Length; Dest->Angle = Src->Angle; Dest->A = Src->A; Dest->B = Src->B; Dest->C = Src->C; } /********************************************************************** * FillABC * * Fill in Protos A, B, C fields based on the X, Y, Angle fields. **********************************************************************/ void FillABC(PROTO Proto) { FLOAT32 Slope, Intercept, Normalizer; Slope = tan (Proto->Angle * 2.0 * PI); Intercept = Proto->Y - Slope * Proto->X; Normalizer = 1.0 / sqrt (Slope * Slope + 1.0); Proto->A = Slope * Normalizer; Proto->B = -Normalizer; Proto->C = Intercept * Normalizer; } /********************************************************************** * FreeClass * * Deallocate the memory consumed by the specified class. **********************************************************************/ void FreeClass(CLASS_TYPE Class) { if (Class) { FreeClassFields(Class); delete Class; } } /********************************************************************** * FreeClassFields * * Deallocate the memory consumed by subfields of the specified class. **********************************************************************/ void FreeClassFields(CLASS_TYPE Class) { int i; if (Class) { if (Class->MaxNumProtos > 0) memfree (Class->Prototypes); if (Class->MaxNumConfigs > 0) { for (i = 0; i < Class->NumConfigs; i++) FreeBitVector (Class->Configurations[i]); memfree (Class->Configurations); } } } /********************************************************************** * NewClass * * Allocate a new class with enough memory to hold the specified number * of prototypes and configurations. **********************************************************************/ CLASS_TYPE NewClass(int NumProtos, int NumConfigs) { CLASS_TYPE Class; Class = new CLASS_STRUCT; if (NumProtos > 0) Class->Prototypes = (PROTO) Emalloc (NumProtos * sizeof (PROTO_STRUCT)); if (NumConfigs > 0) Class->Configurations = (CONFIGS) Emalloc (NumConfigs * sizeof (BIT_VECTOR)); Class->MaxNumProtos = NumProtos; Class->MaxNumConfigs = NumConfigs; Class->NumProtos = 0; Class->NumConfigs = 0; return (Class); } /********************************************************************** * PrintProtos * * Print the list of prototypes in this class type. **********************************************************************/ void PrintProtos(CLASS_TYPE Class) { inT16 Pid; for (Pid = 0; Pid < Class->NumProtos; Pid++) { cprintf ("Proto %d:\t", Pid); PrintProto (ProtoIn (Class, Pid)); cprintf ("\t"); PrintProtoLine (ProtoIn (Class, Pid)); new_line(); } } tesseract-3.04.01/classify/protos.h000066400000000000000000000115041266071204500171740ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: protos.h (Formerly protos.h) * Description: * Author: Mark Seaman, SW Productivity * Created: Fri Oct 16 14:37:00 1987 * Modified: Fri Jul 12 10:06:55 1991 (Dan Johnson) danj@hpgrlj * 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 PROTOS_H #define PROTOS_H /*---------------------------------------------------------------------- I n c l u d e s ----------------------------------------------------------------------*/ #include "bitvec.h" #include "cutil.h" #include "unichar.h" #include "unicity_table.h" #include "params.h" /*---------------------------------------------------------------------- T y p e s ----------------------------------------------------------------------*/ typedef BIT_VECTOR *CONFIGS; typedef struct { FLOAT32 A; FLOAT32 B; FLOAT32 C; FLOAT32 X; FLOAT32 Y; FLOAT32 Angle; FLOAT32 Length; } PROTO_STRUCT; typedef PROTO_STRUCT *PROTO; struct CLASS_STRUCT { CLASS_STRUCT() : NumProtos(0), MaxNumProtos(0), Prototypes(NULL), NumConfigs(0), MaxNumConfigs(0), Configurations(NULL) { } inT16 NumProtos; inT16 MaxNumProtos; PROTO Prototypes; inT16 NumConfigs; inT16 MaxNumConfigs; CONFIGS Configurations; UnicityTableEqEq font_set; }; typedef CLASS_STRUCT *CLASS_TYPE; typedef CLASS_STRUCT *CLASSES; /*---------------------------------------------------------------------- C o n s t a n t s ----------------------------------------------------------------------*/ #define NUMBER_OF_CLASSES MAX_NUM_CLASSES #define Y_OFFSET -40.0 #define FEATURE_SCALE 100.0 /*---------------------------------------------------------------------- V a r i a b l e s ----------------------------------------------------------------------*/ extern CLASS_STRUCT TrainingData[]; extern STRING_VAR_H(classify_training_file, "MicroFeatures", "Training file"); /*---------------------------------------------------------------------- M a c r o s ----------------------------------------------------------------------*/ /** * AddProtoToConfig * * Set a single proto bit in the specified configuration. */ #define AddProtoToConfig(Pid,Config) \ (SET_BIT (Config, Pid)) /** * RemoveProtoFromConfig * * Clear a single proto bit in the specified configuration. */ #define RemoveProtoFromConfig(Pid,Config) \ (reset_bit (Config, Pid)) /** * ClassOfChar * * Return the class of a particular ASCII character value. */ #define ClassOfChar(Char) \ ((TrainingData [Char].NumProtos) ? \ (& TrainingData [Char]) : \ NO_CLASS) /** * ProtoIn * * Choose the selected prototype in this class record. Return the * pointer to it (type PROTO). */ #define ProtoIn(Class,Pid) \ (& (Class)->Prototypes [Pid]) /** * PrintProto * * Print out the contents of a prototype. The 'Proto' argument is of * type 'PROTO'. */ #define PrintProto(Proto) \ (tprintf("X=%4.2f, Y=%4.2f, Length=%4.2f, Angle=%4.2f", \ Proto->X, \ Proto->Y, \ Proto->Length, \ Proto->Angle)) \ /** * PrintProtoLine * * Print out the contents of a prototype. The 'Proto' argument is of * type 'PROTO'. */ #define PrintProtoLine(Proto) \ (cprintf ("A=%4.2f, B=%4.2f, C=%4.2f", \ Proto->A, \ Proto->B, \ Proto->C)) \ /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ int AddConfigToClass(CLASS_TYPE Class); int AddProtoToClass(CLASS_TYPE Class); FLOAT32 ClassConfigLength(CLASS_TYPE Class, BIT_VECTOR Config); FLOAT32 ClassProtoLength(CLASS_TYPE Class); void CopyProto(PROTO Src, PROTO Dest); void FillABC(PROTO Proto); void FreeClass(CLASS_TYPE Class); void FreeClassFields(CLASS_TYPE Class); void InitPrototypes(); CLASS_TYPE NewClass(int NumProtos, int NumConfigs); void PrintProtos(CLASS_TYPE Class); #endif tesseract-3.04.01/classify/sampleiterator.cpp000066400000000000000000000213271266071204500212400ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) // // 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 "sampleiterator.h" #include "indexmapbidi.h" #include "shapetable.h" #include "trainingsample.h" #include "trainingsampleset.h" namespace tesseract { // ================== SampleIterator Implementation ================= SampleIterator::SampleIterator() : charset_map_(NULL), shape_table_(NULL), sample_set_(NULL), randomize_(false), owned_shape_table_(NULL) { num_shapes_ = 0; Begin(); } SampleIterator::~SampleIterator() { Clear(); } void SampleIterator::Clear() { delete owned_shape_table_; owned_shape_table_ = NULL; } // See class comment for arguments. void SampleIterator::Init(const IndexMapBiDi* charset_map, const ShapeTable* shape_table, bool randomize, TrainingSampleSet* sample_set) { Clear(); charset_map_ = charset_map; shape_table_ = shape_table; sample_set_ = sample_set; randomize_ = randomize; if (shape_table_ == NULL && charset_map_ != NULL) { // The caller wishes to iterate by class. The easiest way to do this // is to create a dummy shape_table_ that we will own. int num_fonts = sample_set_->NumFonts(); owned_shape_table_ = new ShapeTable(sample_set_->unicharset()); int charsetsize = sample_set_->unicharset().size(); for (int c = 0; c < charsetsize; ++c) { // We always add a shape for each character to keep the index in sync // with the unichar_id. int shape_id = owned_shape_table_->AddShape(c, 0); for (int f = 1; f < num_fonts; ++f) { if (sample_set_->NumClassSamples(f, c, true) > 0) { owned_shape_table_->AddToShape(shape_id, c, f); } } } shape_table_ = owned_shape_table_; } if (shape_table_ != NULL) { num_shapes_ = shape_table_->NumShapes(); } else { num_shapes_ = randomize ? sample_set_->num_samples() : sample_set_->num_raw_samples(); } Begin(); } // Iterator functions designed for use with a simple for loop: // for (it.Begin(); !it.AtEnd(); it.Next()) { // const TrainingSample& sample = it.GetSample(); // } void SampleIterator::Begin() { shape_index_ = -1; shape_char_index_ = 0; num_shape_chars_ = 0; shape_font_index_ = 0; num_shape_fonts_ = 0; sample_index_ = 0; num_samples_ = 0; // Find the first indexable sample. Next(); } bool SampleIterator::AtEnd() const { return shape_index_ >= num_shapes_; } const TrainingSample& SampleIterator::GetSample() const { if (shape_table_ != NULL) { const UnicharAndFonts* shape_entry = GetShapeEntry(); int char_id = shape_entry->unichar_id; int font_id = shape_entry->font_ids[shape_font_index_]; return *sample_set_->GetSample(font_id, char_id, sample_index_); } else { return *sample_set_->GetSample(shape_index_); } } TrainingSample* SampleIterator::MutableSample() const { if (shape_table_ != NULL) { const UnicharAndFonts* shape_entry = GetShapeEntry(); int char_id = shape_entry->unichar_id; int font_id = shape_entry->font_ids[shape_font_index_]; return sample_set_->MutableSample(font_id, char_id, sample_index_); } else { return sample_set_->mutable_sample(shape_index_); } } // Returns the total index (from the original set of samples) of the current // sample. int SampleIterator::GlobalSampleIndex() const { if (shape_table_ != NULL) { const UnicharAndFonts* shape_entry = GetShapeEntry(); int char_id = shape_entry->unichar_id; int font_id = shape_entry->font_ids[shape_font_index_]; return sample_set_->GlobalSampleIndex(font_id, char_id, sample_index_); } else { return shape_index_; } } // Returns the index of the current sample in compact charset space, so // in a 2-class problem between x and y, the returned indices will all be // 0 or 1, and have nothing to do with the unichar_ids. // If the charset_map_ is NULL, then this is equal to GetSparseClassID(). int SampleIterator::GetCompactClassID() const { return charset_map_ != NULL ? charset_map_->SparseToCompact(shape_index_) : GetSparseClassID(); } // Returns the index of the current sample in sparse charset space, so // in a 2-class problem between x and y, the returned indices will all be // x or y, where x and y may be unichar_ids (no shape_table_) or shape_ids // with a shape_table_. int SampleIterator::GetSparseClassID() const { return shape_table_ != NULL ? shape_index_ : GetSample().class_id(); } // Moves on to the next indexable sample. If the end is reached, leaves // the state such that AtEnd() is true. void SampleIterator::Next() { if (shape_table_ != NULL) { // Next sample in this class/font combination. ++sample_index_; if (sample_index_ < num_samples_) return; // Next font in this class in this shape. sample_index_ = 0; do { ++shape_font_index_; if (shape_font_index_ >= num_shape_fonts_) { // Next unichar in this shape. shape_font_index_ = 0; ++shape_char_index_; if (shape_char_index_ >= num_shape_chars_) { // Find the next shape that is mapped in the charset_map_. shape_char_index_ = 0; do { ++shape_index_; } while (shape_index_ < num_shapes_ && charset_map_ != NULL && charset_map_->SparseToCompact(shape_index_) < 0); if (shape_index_ >= num_shapes_) return; // The end. num_shape_chars_ = shape_table_->GetShape(shape_index_).size(); } } const UnicharAndFonts* shape_entry = GetShapeEntry(); num_shape_fonts_ = shape_entry->font_ids.size(); int char_id = shape_entry->unichar_id; int font_id = shape_entry->font_ids[shape_font_index_]; num_samples_ = sample_set_->NumClassSamples(font_id, char_id, randomize_); } while (num_samples_ == 0); } else { // We are just iterating over the samples. ++shape_index_; } } // Returns the size of the compact charset space. int SampleIterator::CompactCharsetSize() const { return charset_map_ != NULL ? charset_map_->CompactSize() : SparseCharsetSize(); } // Returns the size of the sparse charset space. int SampleIterator::SparseCharsetSize() const { return charset_map_ != NULL ? charset_map_->SparseSize() : (shape_table_ != NULL ? shape_table_->NumShapes() : sample_set_->charsetsize()); } // Apply the supplied feature_space/feature_map transform to all samples // accessed by this iterator. void SampleIterator::MapSampleFeatures(const IntFeatureMap& feature_map) { for (Begin(); !AtEnd(); Next()) { TrainingSample* sample = MutableSample(); sample->MapFeatures(feature_map); } } // Adjust the weights of all the samples to be uniform in the given charset. // Returns the number of samples in the iterator. int SampleIterator::UniformSamples() { int num_good_samples = 0; for (Begin(); !AtEnd(); Next()) { TrainingSample* sample = MutableSample(); sample->set_weight(1.0); ++num_good_samples; } NormalizeSamples(); return num_good_samples; } // Normalize the weights of all the samples in the charset_map so they sum // to 1. Returns the minimum assigned sample weight. double SampleIterator::NormalizeSamples() { double total_weight = 0.0; int sample_count = 0; for (Begin(); !AtEnd(); Next()) { const TrainingSample& sample = GetSample(); total_weight += sample.weight(); ++sample_count; } // Normalize samples. double min_assigned_sample_weight = 1.0; if (total_weight > 0.0) { for (Begin(); !AtEnd(); Next()) { TrainingSample* sample = MutableSample(); double weight = sample->weight() / total_weight; if (weight < min_assigned_sample_weight) min_assigned_sample_weight = weight; sample->set_weight(weight); } } return min_assigned_sample_weight; } // Helper returns the current UnicharAndFont shape_entry. const UnicharAndFonts* SampleIterator::GetShapeEntry() const { const Shape& shape = shape_table_->GetShape(shape_index_); return &shape[shape_char_index_]; } } // namespace tesseract. tesseract-3.04.01/classify/sampleiterator.h000066400000000000000000000165471266071204500207150ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) // // 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_CLASSIFY_SAMPLEITERATOR_H_ #define TESSERACT_CLASSIFY_SAMPLEITERATOR_H_ namespace tesseract { class IndexMapBiDi; class IntFeatureMap; class ShapeTable; class TrainingSample; class TrainingSampleSet; struct UnicharAndFonts; // Iterator class to encapsulate the complex iteration involved in getting // all samples of all shapes needed for a classification problem. // // =====INPUTS TO Init FUNCTION===== // The charset_map defines a subset of the sample_set classes (with a NULL // shape_table, or the shape_table classes if not NULL.) // // The shape_table (if not NULL) defines the mapping from shapes to // font_id/class_id pairs. Each shape is a list of unichar_id and font lists. // // The sample_set holds the samples and provides indexed access to samples // of font_id/class_id pairs. // // If randomize is true, the samples are perturbed slightly, but the // perturbation is guaranteed to be the same for multiple identical // iterations. // // =====DIFFERENT COMBINATIONS OF INPUTS===== // NULL shape_table: // Without a shape_table, everything works in UNICHAR_IDs. // // NULL shape_table, NULL charset_map: // Iterations simply run over the samples in the order the samples occur in the // input files. // GetCompactClassID and GetSparseClassID both return the sample UNICHAR_ID. // // NULL shape_table, non-NULL charset_map: // When shape_table is NULL, the charset_map indexes unichar_ids directly, // and an iteration returns all samples of all chars in the charset_map, which // is a subset of the full unicharset. // The iteration will be in groups of the same unichar_id, in the order // defined by the charset_map. // GetCompactClassID returns the charset_map index of a sample, and // GetSparseClassID returns the sample UNICHAR_ID. // // Non-NULL shape_table: // With a shape_table, samples are grouped according to the shape_table, so // multiple UNICHAR_IDs and fonts may be grouped together, and everything // works in shape_ids. // // Non-NULL shape_table, NULL charset_map. // Iterations simply run over the samples in the order of shape_id. // GetCompactClassID and GetSparseClassID both return the shape_id. // (If you want the unichar_id or font_id, the sample still has them.) // // Non-NULL shape_table, non-NULL charset_map. // When shape_table is not NULL, the charset_map indexes and subsets shapes in // the shape_table, and iterations will be in shape_table order, not // charset_map order. // GetCompactClassID returns the charset_map index of a shape, and // GetSparseClassID returns the shape_id. // // =====What is SampleIterator good for?===== // Inside a classifier training module, the SampleIterator has abstracted away // all the different modes above. // Use the following iteration to train your classifier: // for (it.Begin(); !it.AtEnd(); it.Next()) { // const TrainingSample& sample = it.GetSample(); // int class_id = it.GetCompactClassID(); // Your classifier may or may not be dealing with a shape_table, and may be // dealing with some subset of the character/shape set. It doesn't need to // know and shouldn't care. It is just learning shapes with compact class ids // in the range [0, it.CompactCharsetSize()). class SampleIterator { public: SampleIterator(); ~SampleIterator(); void Clear(); // See class comment for arguments. void Init(const IndexMapBiDi* charset_map, const ShapeTable* shape_table, bool randomize, TrainingSampleSet* sample_set); // Iterator functions designed for use with a simple for loop: // for (it.Begin(); !it.AtEnd(); it.Next()) { // const TrainingSample& sample = it.GetSample(); // int class_id = it.GetCompactClassID(); // ... // } void Begin(); bool AtEnd() const; const TrainingSample& GetSample() const; TrainingSample* MutableSample() const; // Returns the total index (from the original set of samples) of the current // sample. int GlobalSampleIndex() const; // Returns the index of the current sample in compact charset space, so // in a 2-class problem between x and y, the returned indices will all be // 0 or 1, and have nothing to do with the unichar_ids. // If the charset_map_ is NULL, then this is equal to GetSparseClassID(). int GetCompactClassID() const; // Returns the index of the current sample in sparse charset space, so // in a 2-class problem between x and y, the returned indices will all be // x or y, where x and y may be unichar_ids (no shape_table_) or shape_ids // with a shape_table_. int GetSparseClassID() const; // Moves on to the next indexable sample. If the end is reached, leaves // the state such that AtEnd() is true. void Next(); // Returns the size of the compact charset space. int CompactCharsetSize() const; // Returns the size of the sparse charset space. int SparseCharsetSize() const; const IndexMapBiDi& charset_map() const { return *charset_map_; } const ShapeTable* shape_table() const { return shape_table_; } // Sample set operations. const TrainingSampleSet* sample_set() const { return sample_set_; } // A set of functions that do something to all the samples accessed by the // iterator, as it is currently setup. // Apply the supplied feature_space/feature_map transform to all samples // accessed by this iterator. void MapSampleFeatures(const IntFeatureMap& feature_map); // Adjust the weights of all the samples to be uniform in the given charset. // Returns the number of samples in the iterator. int UniformSamples(); // Normalize the weights of all the samples defined by the iterator so they // sum to 1. Returns the minimum assigned sample weight. double NormalizeSamples(); private: // Helper returns the current UnicharAndFont shape_entry. const UnicharAndFonts* GetShapeEntry() const; // Map to subset the actual charset space. const IndexMapBiDi* charset_map_; // Shape table to recombine character classes into shapes const ShapeTable* shape_table_; // The samples to iterate over. TrainingSampleSet* sample_set_; // Flag to control randomizing the sample features. bool randomize_; // Shape table owned by this used to iterate character classes. ShapeTable* owned_shape_table_; // Top-level iteration. Shape index in sparse charset_map space. int shape_index_; int num_shapes_; // Index to the character class within a shape. int shape_char_index_; int num_shape_chars_; // Index to the font within a shape/class pair. int shape_font_index_; int num_shape_fonts_; // The lowest level iteration. sample_index_/num_samples_ counts samples // in the current shape/class/font combination. int sample_index_; int num_samples_; }; } // namespace tesseract. #endif // TESSERACT_CLASSIFY_SAMPLEITERATOR_H_ tesseract-3.04.01/classify/shapeclassifier.cpp000066400000000000000000000215601266071204500213510ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: shapeclassifier.h // Description: Base interface class for classifiers that return a // shape index. // Author: Ray Smith // Created: Thu Dec 15 15:24:27 PST 2011 // // (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. // /////////////////////////////////////////////////////////////////////// #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #include "shapeclassifier.h" #include "genericvector.h" #include "scrollview.h" #include "shapetable.h" #include "svmnode.h" #include "trainingsample.h" #include "tprintf.h" namespace tesseract { // Classifies the given [training] sample, writing to results. // See shapeclassifier.h for a full description. // Default implementation calls the ShapeRating version. int ShapeClassifier::UnicharClassifySample( const TrainingSample& sample, Pix* page_pix, int debug, UNICHAR_ID keep_this, GenericVector* results) { results->truncate(0); GenericVector shape_results; int num_shape_results = ClassifySample(sample, page_pix, debug, keep_this, &shape_results); const ShapeTable* shapes = GetShapeTable(); GenericVector unichar_map; unichar_map.init_to_size(shapes->unicharset().size(), -1); for (int r = 0; r < num_shape_results; ++r) { shapes->AddShapeToResults(shape_results[r], &unichar_map, results); } return results->size(); } // Classifies the given [training] sample, writing to results. // See shapeclassifier.h for a full description. // Default implementation aborts. int ShapeClassifier::ClassifySample(const TrainingSample& sample, Pix* page_pix, int debug, int keep_this, GenericVector* results) { ASSERT_HOST("Must implement ClassifySample!" == NULL); return 0; } // Returns the shape that contains unichar_id that has the best result. // If result is not NULL, it is set with the shape_id and rating. // Does not need to be overridden if ClassifySample respects the keep_this // rule. int ShapeClassifier::BestShapeForUnichar(const TrainingSample& sample, Pix* page_pix, UNICHAR_ID unichar_id, ShapeRating* result) { GenericVector results; const ShapeTable* shapes = GetShapeTable(); int num_results = ClassifySample(sample, page_pix, 0, unichar_id, &results); for (int r = 0; r < num_results; ++r) { if (shapes->GetShape(results[r].shape_id).ContainsUnichar(unichar_id)) { if (result != NULL) *result = results[r]; return results[r].shape_id; } } return -1; } // Provides access to the UNICHARSET that this classifier works with. // Only needs to be overridden if GetShapeTable() can return NULL. const UNICHARSET& ShapeClassifier::GetUnicharset() const { return GetShapeTable()->unicharset(); } // Visual debugger classifies the given sample, displays the results and // solicits user input to display other classifications. Returns when // the user has finished with debugging the sample. // Probably doesn't need to be overridden if the subclass provides // DisplayClassifyAs. void ShapeClassifier::DebugDisplay(const TrainingSample& sample, Pix* page_pix, UNICHAR_ID unichar_id) { #ifndef GRAPHICS_DISABLED static ScrollView* terminator = NULL; if (terminator == NULL) { terminator = new ScrollView("XIT", 0, 0, 50, 50, 50, 50, true); } ScrollView* debug_win = CreateFeatureSpaceWindow("ClassifierDebug", 0, 0); // Provide a right-click menu to choose the class. SVMenuNode* popup_menu = new SVMenuNode(); popup_menu->AddChild("Choose class to debug", 0, "x", "Class to debug"); popup_menu->BuildMenu(debug_win, false); // Display the features in green. const INT_FEATURE_STRUCT* features = sample.features(); int num_features = sample.num_features(); for (int f = 0; f < num_features; ++f) { RenderIntFeature(debug_win, &features[f], ScrollView::GREEN); } debug_win->Update(); GenericVector results; // Debug classification until the user quits. const UNICHARSET& unicharset = GetUnicharset(); SVEvent* ev; SVEventType ev_type; do { PointerVector windows; if (unichar_id >= 0) { tprintf("Debugging class %d = %s\n", unichar_id, unicharset.id_to_unichar(unichar_id)); UnicharClassifySample(sample, page_pix, 1, unichar_id, &results); DisplayClassifyAs(sample, page_pix, unichar_id, 1, &windows); } else { tprintf("Invalid unichar_id: %d\n", unichar_id); UnicharClassifySample(sample, page_pix, 1, -1, &results); } if (unichar_id >= 0) { tprintf("Debugged class %d = %s\n", unichar_id, unicharset.id_to_unichar(unichar_id)); } tprintf("Right-click in ClassifierDebug window to choose debug class,"); tprintf(" Left-click or close window to quit...\n"); UNICHAR_ID old_unichar_id; do { old_unichar_id = unichar_id; ev = debug_win->AwaitEvent(SVET_ANY); ev_type = ev->type; if (ev_type == SVET_POPUP) { if (unicharset.contains_unichar(ev->parameter)) { unichar_id = unicharset.unichar_to_id(ev->parameter); } else { tprintf("Char class '%s' not found in unicharset", ev->parameter); } } delete ev; } while (unichar_id == old_unichar_id && ev_type != SVET_CLICK && ev_type != SVET_DESTROY); } while (ev_type != SVET_CLICK && ev_type != SVET_DESTROY); delete debug_win; #endif // GRAPHICS_DISABLED } // Displays classification as the given shape_id. Creates as many windows // as it feels fit, using index as a guide for placement. Adds any created // windows to the windows output and returns a new index that may be used // by any subsequent classifiers. Caller waits for the user to view and // then destroys the windows by clearing the vector. int ShapeClassifier::DisplayClassifyAs( const TrainingSample& sample, Pix* page_pix, UNICHAR_ID unichar_id, int index, PointerVector* windows) { // Does nothing in the default implementation. return index; } // Prints debug information on the results. void ShapeClassifier::UnicharPrintResults( const char* context, const GenericVector& results) const { tprintf("%s\n", context); for (int i = 0; i < results.size(); ++i) { tprintf("%g: c_id=%d=%s", results[i].rating, results[i].unichar_id, GetUnicharset().id_to_unichar(results[i].unichar_id)); if (results[i].fonts.size() != 0) { tprintf(" Font Vector:"); for (int f = 0; f < results[i].fonts.size(); ++f) { tprintf(" %d", results[i].fonts[f]); } } tprintf("\n"); } } void ShapeClassifier::PrintResults( const char* context, const GenericVector& results) const { tprintf("%s\n", context); for (int i = 0; i < results.size(); ++i) { tprintf("%g:", results[i].rating); if (results[i].joined) tprintf("[J]"); if (results[i].broken) tprintf("[B]"); tprintf(" %s\n", GetShapeTable()->DebugStr(results[i].shape_id).string()); } } // Removes any result that has all its unichars covered by a better choice, // regardless of font. void ShapeClassifier::FilterDuplicateUnichars( GenericVector* results) const { GenericVector filtered_results; // Copy results to filtered results and knock out duplicate unichars. const ShapeTable* shapes = GetShapeTable(); for (int r = 0; r < results->size(); ++r) { if (r > 0) { const Shape& shape_r = shapes->GetShape((*results)[r].shape_id); int c; for (c = 0; c < shape_r.size(); ++c) { int unichar_id = shape_r[c].unichar_id; int s; for (s = 0; s < r; ++s) { const Shape& shape_s = shapes->GetShape((*results)[s].shape_id); if (shape_s.ContainsUnichar(unichar_id)) break; // We found unichar_id. } if (s == r) break; // We didn't find unichar_id. } if (c == shape_r.size()) continue; // We found all the unichar ids in previous answers. } filtered_results.push_back((*results)[r]); } *results = filtered_results; } } // namespace tesseract. tesseract-3.04.01/classify/shapeclassifier.h000066400000000000000000000131071266071204500210140ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: shapeclassifier.h // Description: Base interface class for classifiers that return a // shape index. // Author: Ray Smith // Created: Tue Sep 13 11:26:32 PDT 2011 // // (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_CLASSIFY_SHAPECLASSIFIER_H_ #define TESSERACT_CLASSIFY_SHAPECLASSIFIER_H_ #include "unichar.h" template class GenericVector; struct Pix; class ScrollView; class UNICHARSET; namespace tesseract { template class PointerVector; struct ShapeRating; class ShapeTable; class TrainingSample; class TrainingSampleSet; struct UnicharRating; // Interface base class for classifiers that produce ShapeRating results. class ShapeClassifier { public: virtual ~ShapeClassifier() {} // Classifies the given [training] sample, writing to results. // If page_pix is not NULL, the overriding function may call // sample.GetSamplePix(padding, page_pix) to get an image of the sample // padded (with real image data) by the given padding to extract features // from the image of the character. Other members of TrainingSample: // features(), micro_features(), cn_feature(), geo_feature() may be used // to get the appropriate tesseract features. // If debug is non-zero, then various degrees of classifier dependent debug // information is provided. // If keep_this (a UNICHAR_ID) is >= 0, then the results should always // contain keep_this, and (if possible) anything of intermediate confidence. // (Used for answering "Why didn't it get that right?" questions.) It must // be a UNICHAR_ID as the callers have no clue how to choose the best shape // that may contain a desired answer. // The return value is the number of classes saved in results. // NOTE that overriding functions MUST clear and sort the results by // descending rating unless the classifier is working with a team of such // classifiers. // NOTE: Neither overload of ClassifySample is pure, but at least one must // be overridden by a classifier in order for it to do anything. virtual int UnicharClassifySample(const TrainingSample& sample, Pix* page_pix, int debug, UNICHAR_ID keep_this, GenericVector* results); protected: virtual int ClassifySample(const TrainingSample& sample, Pix* page_pix, int debug, UNICHAR_ID keep_this, GenericVector* results); public: // Returns the shape that contains unichar_id that has the best result. // If result is not NULL, it is set with the shape_id and rating. // Returns -1 if ClassifySample fails to provide any result containing // unichar_id. BestShapeForUnichar does not need to be overridden if // ClassifySample respects the keep_this rule. virtual int BestShapeForUnichar(const TrainingSample& sample, Pix* page_pix, UNICHAR_ID unichar_id, ShapeRating* result); // Provides access to the ShapeTable that this classifier works with. virtual const ShapeTable* GetShapeTable() const = 0; // Provides access to the UNICHARSET that this classifier works with. // Must be overridden IFF GetShapeTable() returns NULL. virtual const UNICHARSET& GetUnicharset() const; // Visual debugger classifies the given sample, displays the results and // solicits user input to display other classifications. Returns when // the user has finished with debugging the sample. // Probably doesn't need to be overridden if the subclass provides // DisplayClassifyAs. virtual void DebugDisplay(const TrainingSample& sample, Pix* page_pix, UNICHAR_ID unichar_id); // Displays classification as the given unichar_id. Creates as many windows // as it feels fit, using index as a guide for placement. Adds any created // windows to the windows output and returns a new index that may be used // by any subsequent classifiers. Caller waits for the user to view and // then destroys the windows by clearing the vector. virtual int DisplayClassifyAs(const TrainingSample& sample, Pix* page_pix, UNICHAR_ID unichar_id, int index, PointerVector* windows); // Prints debug information on the results. context is some introductory/title // message. virtual void UnicharPrintResults( const char* context, const GenericVector& results) const; virtual void PrintResults(const char* context, const GenericVector& results) const; protected: // Removes any result that has all its unichars covered by a better choice, // regardless of font. void FilterDuplicateUnichars(GenericVector* results) const; }; } // namespace tesseract. #endif // TESSERACT_CLASSIFY_SHAPECLASSIFIER_H_ tesseract-3.04.01/classify/shapetable.cpp000066400000000000000000000630351266071204500203170ustar00rootroot00000000000000// Copyright 2010 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: shapetable.cpp // Description: Class to map a classifier shape index to unicharset // indices and font indices. // Author: Ray Smith // Created: Tue Nov 02 15:31:32 PDT 2010 // // (C) Copyright 2010, 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 "shapetable.h" #include "bitvector.h" #include "fontinfo.h" #include "intfeaturespace.h" #include "strngs.h" #include "unicharset.h" #include "unicity_table.h" namespace tesseract { // Helper function to get the index of the first result with the required // unichar_id. If the results are sorted by rating, this will also be the // best result with the required unichar_id. // Returns -1 if the unichar_id is not found int ShapeRating::FirstResultWithUnichar( const GenericVector& results, const ShapeTable& shape_table, UNICHAR_ID unichar_id) { for (int r = 0; r < results.size(); ++r) { int shape_id = results[r].shape_id; const Shape& shape = shape_table.GetShape(shape_id); if (shape.ContainsUnichar(unichar_id)) { return r; } } return -1; } // Helper function to get the index of the first result with the required // unichar_id. If the results are sorted by rating, this will also be the // best result with the required unichar_id. // Returns -1 if the unichar_id is not found int UnicharRating::FirstResultWithUnichar( const GenericVector& results, UNICHAR_ID unichar_id) { for (int r = 0; r < results.size(); ++r) { if (results[r].unichar_id == unichar_id) return r; } return -1; } // Writes to the given file. Returns false in case of error. bool UnicharAndFonts::Serialize(FILE* fp) const { if (fwrite(&unichar_id, sizeof(unichar_id), 1, fp) != 1) return false; if (!font_ids.Serialize(fp)) return false; return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool UnicharAndFonts::DeSerialize(bool swap, FILE* fp) { if (fread(&unichar_id, sizeof(unichar_id), 1, fp) != 1) return false; if (swap) ReverseN(&unichar_id, sizeof(unichar_id)); if (!font_ids.DeSerialize(swap, fp)) return false; return true; } // Sort function to sort a pair of UnicharAndFonts by unichar_id. int UnicharAndFonts::SortByUnicharId(const void* v1, const void* v2) { const UnicharAndFonts* p1 = reinterpret_cast(v1); const UnicharAndFonts* p2 = reinterpret_cast(v2); return p1->unichar_id - p2->unichar_id; } // Writes to the given file. Returns false in case of error. bool Shape::Serialize(FILE* fp) const { uinT8 sorted = unichars_sorted_; if (fwrite(&sorted, sizeof(sorted), 1, fp) != 1) return false; if (!unichars_.SerializeClasses(fp)) return false; return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool Shape::DeSerialize(bool swap, FILE* fp) { uinT8 sorted; if (fread(&sorted, sizeof(sorted), 1, fp) != 1) return false; unichars_sorted_ = sorted != 0; if (!unichars_.DeSerializeClasses(swap, fp)) return false; return true; } // Adds a font_id for the given unichar_id. If the unichar_id is not // in the shape, it is added. void Shape::AddToShape(int unichar_id, int font_id) { for (int c = 0; c < unichars_.size(); ++c) { if (unichars_[c].unichar_id == unichar_id) { // Found the unichar in the shape table. GenericVector& font_list = unichars_[c].font_ids; for (int f = 0; f < font_list.size(); ++f) { if (font_list[f] == font_id) return; // Font is already there. } font_list.push_back(font_id); return; } } // Unichar_id is not in shape, so add it to shape. unichars_.push_back(UnicharAndFonts(unichar_id, font_id)); unichars_sorted_ = unichars_.size() <= 1; } // Adds everything in other to this. void Shape::AddShape(const Shape& other) { for (int c = 0; c < other.unichars_.size(); ++c) { for (int f = 0; f < other.unichars_[c].font_ids.size(); ++f) { AddToShape(other.unichars_[c].unichar_id, other.unichars_[c].font_ids[f]); } } unichars_sorted_ = unichars_.size() <= 1; } // Returns true if the shape contains the given unichar_id, font_id pair. bool Shape::ContainsUnicharAndFont(int unichar_id, int font_id) const { for (int c = 0; c < unichars_.size(); ++c) { if (unichars_[c].unichar_id == unichar_id) { // Found the unichar, so look for the font. GenericVector& font_list = unichars_[c].font_ids; for (int f = 0; f < font_list.size(); ++f) { if (font_list[f] == font_id) return true; } return false; } } return false; } // Returns true if the shape contains the given unichar_id, ignoring font. bool Shape::ContainsUnichar(int unichar_id) const { for (int c = 0; c < unichars_.size(); ++c) { if (unichars_[c].unichar_id == unichar_id) { return true; } } return false; } // Returns true if the shape contains the given font, ignoring unichar_id. bool Shape::ContainsFont(int font_id) const { for (int c = 0; c < unichars_.size(); ++c) { GenericVector& font_list = unichars_[c].font_ids; for (int f = 0; f < font_list.size(); ++f) { if (font_list[f] == font_id) return true; } } return false; } // Returns true if the shape contains the given font properties, ignoring // unichar_id. bool Shape::ContainsFontProperties(const FontInfoTable& font_table, uinT32 properties) const { for (int c = 0; c < unichars_.size(); ++c) { GenericVector& font_list = unichars_[c].font_ids; for (int f = 0; f < font_list.size(); ++f) { if (font_table.get(font_list[f]).properties == properties) return true; } } return false; } // Returns true if the shape contains multiple different font properties, // ignoring unichar_id. bool Shape::ContainsMultipleFontProperties( const FontInfoTable& font_table) const { uinT32 properties = font_table.get(unichars_[0].font_ids[0]).properties; for (int c = 0; c < unichars_.size(); ++c) { GenericVector& font_list = unichars_[c].font_ids; for (int f = 0; f < font_list.size(); ++f) { if (font_table.get(font_list[f]).properties != properties) return true; } } return false; } // Returns true if this shape is equal to other (ignoring order of unichars // and fonts). bool Shape::operator==(const Shape& other) const { return IsSubsetOf(other) && other.IsSubsetOf(*this); } // Returns true if this is a subset (including equal) of other. bool Shape::IsSubsetOf(const Shape& other) const { for (int c = 0; c < unichars_.size(); ++c) { int unichar_id = unichars_[c].unichar_id; const GenericVector& font_list = unichars_[c].font_ids; for (int f = 0; f < font_list.size(); ++f) { if (!other.ContainsUnicharAndFont(unichar_id, font_list[f])) return false; } } return true; } // Returns true if the lists of unichar ids are the same in this and other, // ignoring fonts. // NOT const, as it will sort the unichars on demand. bool Shape::IsEqualUnichars(Shape* other) { if (unichars_.size() != other->unichars_.size()) return false; if (!unichars_sorted_) SortUnichars(); if (!other->unichars_sorted_) other->SortUnichars(); for (int c = 0; c < unichars_.size(); ++c) { if (unichars_[c].unichar_id != other->unichars_[c].unichar_id) return false; } return true; } // Sorts the unichars_ vector by unichar. void Shape::SortUnichars() { unichars_.sort(UnicharAndFonts::SortByUnicharId); unichars_sorted_ = true; } ShapeTable::ShapeTable() : unicharset_(NULL), num_fonts_(0) { } ShapeTable::ShapeTable(const UNICHARSET& unicharset) : unicharset_(&unicharset), num_fonts_(0) { } // Writes to the given file. Returns false in case of error. bool ShapeTable::Serialize(FILE* fp) const { if (!shape_table_.Serialize(fp)) return false; return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool ShapeTable::DeSerialize(bool swap, FILE* fp) { if (!shape_table_.DeSerialize(swap, fp)) return false; num_fonts_ = 0; return true; } // Returns the number of fonts used in this ShapeTable, computing it if // necessary. int ShapeTable::NumFonts() const { if (num_fonts_ <= 0) { for (int shape_id = 0; shape_id < shape_table_.size(); ++shape_id) { const Shape& shape = *shape_table_[shape_id]; for (int c = 0; c < shape.size(); ++c) { for (int f = 0; f < shape[c].font_ids.size(); ++f) { if (shape[c].font_ids[f] >= num_fonts_) num_fonts_ = shape[c].font_ids[f] + 1; } } } } return num_fonts_; } // Re-indexes the class_ids in the shapetable according to the given map. // Useful in conjunction with set_unicharset. void ShapeTable::ReMapClassIds(const GenericVector& unicharset_map) { for (int shape_id = 0; shape_id < shape_table_.size(); ++shape_id) { Shape* shape = shape_table_[shape_id]; for (int c = 0; c < shape->size(); ++c) { shape->SetUnicharId(c, unicharset_map[(*shape)[c].unichar_id]); } } } // Returns a string listing the classes/fonts in a shape. STRING ShapeTable::DebugStr(int shape_id) const { if (shape_id < 0 || shape_id >= shape_table_.size()) return STRING("INVALID_UNICHAR_ID"); const Shape& shape = GetShape(shape_id); STRING result; result.add_str_int("Shape", shape_id); if (shape.size() > 100) { result.add_str_int(" Num unichars=", shape.size()); return result; } for (int c = 0; c < shape.size(); ++c) { result.add_str_int(" c_id=", shape[c].unichar_id); result += "="; result += unicharset_->id_to_unichar(shape[c].unichar_id); if (shape.size() < 10) { result.add_str_int(", ", shape[c].font_ids.size()); result += " fonts ="; int num_fonts = shape[c].font_ids.size(); if (num_fonts > 10) { result.add_str_int(" ", shape[c].font_ids[0]); result.add_str_int(" ... ", shape[c].font_ids[num_fonts - 1]); } else { for (int f = 0; f < num_fonts; ++f) { result.add_str_int(" ", shape[c].font_ids[f]); } } } } return result; } // Returns a debug string summarizing the table. STRING ShapeTable::SummaryStr() const { int max_unichars = 0; int num_multi_shapes = 0; int num_master_shapes = 0; for (int s = 0; s < shape_table_.size(); ++s) { if (MasterDestinationIndex(s) != s) continue; ++num_master_shapes; int shape_size = GetShape(s).size(); if (shape_size > 1) ++num_multi_shapes; if (shape_size > max_unichars) max_unichars = shape_size; } STRING result; result.add_str_int("Number of shapes = ", num_master_shapes); result.add_str_int(" max unichars = ", max_unichars); result.add_str_int(" number with multiple unichars = ", num_multi_shapes); return result; } // Adds a new shape starting with the given unichar_id and font_id. // Returns the assigned index. int ShapeTable::AddShape(int unichar_id, int font_id) { int index = shape_table_.size(); Shape* shape = new Shape; shape->AddToShape(unichar_id, font_id); shape_table_.push_back(shape); num_fonts_ = MAX(num_fonts_, font_id + 1); return index; } // Adds a copy of the given shape unless it is already present. // Returns the assigned index or index of existing shape if already present. int ShapeTable::AddShape(const Shape& other) { int index; for (index = 0; index < shape_table_.size() && !(other == *shape_table_[index]); ++index) continue; if (index == shape_table_.size()) { Shape* shape = new Shape(other); shape_table_.push_back(shape); } num_fonts_ = 0; return index; } // Removes the shape given by the shape index. void ShapeTable::DeleteShape(int shape_id) { delete shape_table_[shape_id]; shape_table_[shape_id] = NULL; shape_table_.remove(shape_id); } // Adds a font_id to the given existing shape index for the given // unichar_id. If the unichar_id is not in the shape, it is added. void ShapeTable::AddToShape(int shape_id, int unichar_id, int font_id) { Shape& shape = *shape_table_[shape_id]; shape.AddToShape(unichar_id, font_id); num_fonts_ = MAX(num_fonts_, font_id + 1); } // Adds the given shape to the existing shape with the given index. void ShapeTable::AddShapeToShape(int shape_id, const Shape& other) { Shape& shape = *shape_table_[shape_id]; shape.AddShape(other); num_fonts_ = 0; } // Returns the id of the shape that contains the given unichar and font. // If not found, returns -1. // If font_id < 0, the font_id is ignored and the first shape that matches // the unichar_id is returned. int ShapeTable::FindShape(int unichar_id, int font_id) const { for (int s = 0; s < shape_table_.size(); ++s) { const Shape& shape = GetShape(s); for (int c = 0; c < shape.size(); ++c) { if (shape[c].unichar_id == unichar_id) { if (font_id < 0) return s; // We don't care about the font. for (int f = 0; f < shape[c].font_ids.size(); ++f) { if (shape[c].font_ids[f] == font_id) return s; } } } } return -1; } // Returns the first unichar_id and font_id in the given shape. void ShapeTable::GetFirstUnicharAndFont(int shape_id, int* unichar_id, int* font_id) const { const UnicharAndFonts& unichar_and_fonts = (*shape_table_[shape_id])[0]; *unichar_id = unichar_and_fonts.unichar_id; *font_id = unichar_and_fonts.font_ids[0]; } // Expands all the classes/fonts in the shape individually to build // a ShapeTable. int ShapeTable::BuildFromShape(const Shape& shape, const ShapeTable& master_shapes) { BitVector shape_map(master_shapes.NumShapes()); for (int u_ind = 0; u_ind < shape.size(); ++u_ind) { for (int f_ind = 0; f_ind < shape[u_ind].font_ids.size(); ++f_ind) { int c = shape[u_ind].unichar_id; int f = shape[u_ind].font_ids[f_ind]; int master_id = master_shapes.FindShape(c, f); if (master_id >= 0) { shape_map.SetBit(master_id); } else if (FindShape(c, f) < 0) { AddShape(c, f); } } } int num_masters = 0; for (int s = 0; s < master_shapes.NumShapes(); ++s) { if (shape_map[s]) { AddShape(master_shapes.GetShape(s)); ++num_masters; } } return num_masters; } // Returns true if the shapes are already merged. bool ShapeTable::AlreadyMerged(int shape_id1, int shape_id2) const { return MasterDestinationIndex(shape_id1) == MasterDestinationIndex(shape_id2); } // Returns true if any shape contains multiple unichars. bool ShapeTable::AnyMultipleUnichars() const { int num_shapes = NumShapes(); for (int s1 = 0; s1 < num_shapes; ++s1) { if (MasterDestinationIndex(s1) != s1) continue; if (GetShape(s1).size() > 1) return true; } return false; } // Returns the maximum number of unichars over all shapes. int ShapeTable::MaxNumUnichars() const { int max_num_unichars = 0; int num_shapes = NumShapes(); for (int s = 0; s < num_shapes; ++s) { if (GetShape(s).size() > max_num_unichars) max_num_unichars = GetShape(s).size(); } return max_num_unichars; } // Merges shapes with a common unichar over the [start, end) interval. // Assumes single unichar per shape. void ShapeTable::ForceFontMerges(int start, int end) { for (int s1 = start; s1 < end; ++s1) { if (MasterDestinationIndex(s1) == s1 && GetShape(s1).size() == 1) { int unichar_id = GetShape(s1)[0].unichar_id; for (int s2 = s1 + 1; s2 < end; ++s2) { if (MasterDestinationIndex(s2) == s2 && GetShape(s2).size() == 1 && unichar_id == GetShape(s2)[0].unichar_id) { MergeShapes(s1, s2); } } } } ShapeTable compacted(*unicharset_); compacted.AppendMasterShapes(*this, NULL); *this = compacted; } // Returns the number of unichars in the master shape. int ShapeTable::MasterUnicharCount(int shape_id) const { int master_id = MasterDestinationIndex(shape_id); return GetShape(master_id).size(); } // Returns the sum of the font counts in the master shape. int ShapeTable::MasterFontCount(int shape_id) const { int master_id = MasterDestinationIndex(shape_id); const Shape& shape = GetShape(master_id); int font_count = 0; for (int c = 0; c < shape.size(); ++c) { font_count += shape[c].font_ids.size(); } return font_count; } // Returns the number of unichars that would result from merging the shapes. int ShapeTable::MergedUnicharCount(int shape_id1, int shape_id2) const { // Do it the easy way for now. int master_id1 = MasterDestinationIndex(shape_id1); int master_id2 = MasterDestinationIndex(shape_id2); Shape combined_shape(*shape_table_[master_id1]); combined_shape.AddShape(*shape_table_[master_id2]); return combined_shape.size(); } // Merges two shape_ids, leaving shape_id2 marked as merged. void ShapeTable::MergeShapes(int shape_id1, int shape_id2) { int master_id1 = MasterDestinationIndex(shape_id1); int master_id2 = MasterDestinationIndex(shape_id2); // Point master_id2 (and all merged shapes) to master_id1. shape_table_[master_id2]->set_destination_index(master_id1); // Add all the shapes of master_id2 to master_id1. shape_table_[master_id1]->AddShape(*shape_table_[master_id2]); } // Swaps two shape_ids. void ShapeTable::SwapShapes(int shape_id1, int shape_id2) { Shape* tmp = shape_table_[shape_id1]; shape_table_[shape_id1] = shape_table_[shape_id2]; shape_table_[shape_id2] = tmp; } // Returns the destination of this shape, (if merged), taking into account // the fact that the destination may itself have been merged. int ShapeTable::MasterDestinationIndex(int shape_id) const { int dest_id = shape_table_[shape_id]->destination_index(); if (dest_id == shape_id || dest_id < 0) return shape_id; // Is master already. int master_id = shape_table_[dest_id]->destination_index(); if (master_id == dest_id || master_id < 0) return dest_id; // Dest is the master and shape_id points to it. master_id = MasterDestinationIndex(master_id); return master_id; } // Returns false if the unichars in neither shape is a subset of the other. bool ShapeTable::SubsetUnichar(int shape_id1, int shape_id2) const { const Shape& shape1 = GetShape(shape_id1); const Shape& shape2 = GetShape(shape_id2); int c1, c2; for (c1 = 0; c1 < shape1.size(); ++c1) { int unichar_id1 = shape1[c1].unichar_id; if (!shape2.ContainsUnichar(unichar_id1)) break; } for (c2 = 0; c2 < shape2.size(); ++c2) { int unichar_id2 = shape2[c2].unichar_id; if (!shape1.ContainsUnichar(unichar_id2)) break; } return c1 == shape1.size() || c2 == shape2.size(); } // Returns false if the unichars in neither shape is a subset of the other. bool ShapeTable::MergeSubsetUnichar(int merge_id1, int merge_id2, int shape_id) const { const Shape& merge1 = GetShape(merge_id1); const Shape& merge2 = GetShape(merge_id2); const Shape& shape = GetShape(shape_id); int cm1, cm2, cs; for (cs = 0; cs < shape.size(); ++cs) { int unichar_id = shape[cs].unichar_id; if (!merge1.ContainsUnichar(unichar_id) && !merge2.ContainsUnichar(unichar_id)) break; // Shape is not a subset of the merge. } for (cm1 = 0; cm1 < merge1.size(); ++cm1) { int unichar_id1 = merge1[cm1].unichar_id; if (!shape.ContainsUnichar(unichar_id1)) break; // Merge is not a subset of shape } for (cm2 = 0; cm2 < merge2.size(); ++cm2) { int unichar_id2 = merge2[cm2].unichar_id; if (!shape.ContainsUnichar(unichar_id2)) break; // Merge is not a subset of shape } return cs == shape.size() || (cm1 == merge1.size() && cm2 == merge2.size()); } // Returns true if the unichar sets are equal between the shapes. bool ShapeTable::EqualUnichars(int shape_id1, int shape_id2) const { const Shape& shape1 = GetShape(shape_id1); const Shape& shape2 = GetShape(shape_id2); for (int c1 = 0; c1 < shape1.size(); ++c1) { int unichar_id1 = shape1[c1].unichar_id; if (!shape2.ContainsUnichar(unichar_id1)) return false; } for (int c2 = 0; c2 < shape2.size(); ++c2) { int unichar_id2 = shape2[c2].unichar_id; if (!shape1.ContainsUnichar(unichar_id2)) return false; } return true; } // Returns true if the unichar sets are equal between the shapes. bool ShapeTable::MergeEqualUnichars(int merge_id1, int merge_id2, int shape_id) const { const Shape& merge1 = GetShape(merge_id1); const Shape& merge2 = GetShape(merge_id2); const Shape& shape = GetShape(shape_id); for (int cs = 0; cs < shape.size(); ++cs) { int unichar_id = shape[cs].unichar_id; if (!merge1.ContainsUnichar(unichar_id) && !merge2.ContainsUnichar(unichar_id)) return false; // Shape has a unichar that appears in neither merge. } for (int cm1 = 0; cm1 < merge1.size(); ++cm1) { int unichar_id1 = merge1[cm1].unichar_id; if (!shape.ContainsUnichar(unichar_id1)) return false; // Merge has a unichar that is not in shape. } for (int cm2 = 0; cm2 < merge2.size(); ++cm2) { int unichar_id2 = merge2[cm2].unichar_id; if (!shape.ContainsUnichar(unichar_id2)) return false; // Merge has a unichar that is not in shape. } return true; } // Returns true if there is a common unichar between the shapes. bool ShapeTable::CommonUnichars(int shape_id1, int shape_id2) const { const Shape& shape1 = GetShape(shape_id1); const Shape& shape2 = GetShape(shape_id2); for (int c1 = 0; c1 < shape1.size(); ++c1) { int unichar_id1 = shape1[c1].unichar_id; if (shape2.ContainsUnichar(unichar_id1)) return true; } return false; } // Returns true if there is a common font id between the shapes. bool ShapeTable::CommonFont(int shape_id1, int shape_id2) const { const Shape& shape1 = GetShape(shape_id1); const Shape& shape2 = GetShape(shape_id2); for (int c1 = 0; c1 < shape1.size(); ++c1) { const GenericVector& font_list1 = shape1[c1].font_ids; for (int f = 0; f < font_list1.size(); ++f) { if (shape2.ContainsFont(font_list1[f])) return true; } } return false; } // Appends the master shapes from other to this. // If not NULL, shape_map is set to map other shape_ids to this's shape_ids. void ShapeTable::AppendMasterShapes(const ShapeTable& other, GenericVector* shape_map) { if (shape_map != NULL) shape_map->init_to_size(other.NumShapes(), -1); for (int s = 0; s < other.shape_table_.size(); ++s) { if (other.shape_table_[s]->destination_index() < 0) { int index = AddShape(*other.shape_table_[s]); if (shape_map != NULL) (*shape_map)[s] = index; } } } // Returns the number of master shapes remaining after merging. int ShapeTable::NumMasterShapes() const { int num_shapes = 0; for (int s = 0; s < shape_table_.size(); ++s) { if (shape_table_[s]->destination_index() < 0) ++num_shapes; } return num_shapes; } // Adds the unichars of the given shape_id to the vector of results. Any // unichar_id that is already present just has the fonts added to the // font set for that result without adding a new entry in the vector. // NOTE: it is assumed that the results are given to this function in order // of decreasing rating. // The unichar_map vector indicates the index of the results entry containing // each unichar, or -1 if the unichar is not yet included in results. void ShapeTable::AddShapeToResults(const ShapeRating& shape_rating, GenericVector* unichar_map, GenericVector* results)const { if (shape_rating.joined) { AddUnicharToResults(UNICHAR_JOINED, shape_rating.rating, unichar_map, results); } if (shape_rating.broken) { AddUnicharToResults(UNICHAR_BROKEN, shape_rating.rating, unichar_map, results); } const Shape& shape = GetShape(shape_rating.shape_id); for (int u = 0; u < shape.size(); ++u) { int result_index = AddUnicharToResults(shape[u].unichar_id, shape_rating.rating, unichar_map, results); for (int f = 0; f < shape[u].font_ids.size(); ++f) { (*results)[result_index].fonts.push_back( ScoredFont(shape[u].font_ids[f], IntCastRounded(shape_rating.rating * MAX_INT16))); } } } // Adds the given unichar_id to the results if needed, updating unichar_map // and returning the index of unichar in results. int ShapeTable::AddUnicharToResults( int unichar_id, float rating, GenericVector* unichar_map, GenericVector* results) const { int result_index = unichar_map->get(unichar_id); if (result_index < 0) { UnicharRating result(unichar_id, rating); result_index = results->push_back(result); (*unichar_map)[unichar_id] = result_index; } return result_index; } } // namespace tesseract tesseract-3.04.01/classify/shapetable.h000066400000000000000000000412371266071204500177640ustar00rootroot00000000000000// Copyright 2010 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: shapetable.h // Description: Class to map a classifier shape index to unicharset // indices and font indices. // Author: Ray Smith // Created: Thu Oct 28 17:46:32 PDT 2010 // // (C) Copyright 2010, 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_CLASSIFY_SHAPETABLE_H_ #define TESSERACT_CLASSIFY_SHAPETABLE_H_ #include "bitvector.h" #include "fontinfo.h" #include "genericheap.h" #include "genericvector.h" #include "intmatcher.h" class STRING; class UNICHARSET; namespace tesseract { class ShapeTable; // Simple struct to hold a single classifier unichar selection, a corresponding // rating, and a list of appropriate fonts. struct UnicharRating { UnicharRating() : unichar_id(0), rating(0.0f), adapted(false), config(0), feature_misses(0) {} UnicharRating(int u, float r) : unichar_id(u), rating(r), adapted(false), config(0), feature_misses(0) {} // Print debug info. void Print() const { tprintf("Unichar-id=%d, rating=%g, adapted=%d, config=%d, misses=%d," " %d fonts\n", unichar_id, rating, adapted, config, feature_misses, fonts.size()); } // Sort function to sort ratings appropriately by descending rating. static int SortDescendingRating(const void* t1, const void* t2) { const UnicharRating* a = reinterpret_cast(t1); const UnicharRating* b = reinterpret_cast(t2); if (a->rating > b->rating) { return -1; } else if (a->rating < b->rating) { return 1; } else { return a->unichar_id - b->unichar_id; } } // Helper function to get the index of the first result with the required // unichar_id. If the results are sorted by rating, this will also be the // best result with the required unichar_id. // Returns -1 if the unichar_id is not found static int FirstResultWithUnichar(const GenericVector& results, UNICHAR_ID unichar_id); // Index into some UNICHARSET table indicates the class of the answer. UNICHAR_ID unichar_id; // Rating from classifier with 1.0 perfect and 0.0 impossible. // Call it a probability if you must. float rating; // True if this result is from the adaptive classifier. bool adapted; // Index of best matching font configuration of result. uinT8 config; // Number of features that were total misses - were liked by no classes. uinT16 feature_misses; // Unsorted collection of fontinfo ids and scores. Note that a raw result // from the IntegerMatch will contain config ids, that require transforming // to fontinfo ids via fontsets and (possibly) shapetable. GenericVector fonts; }; // Classifier result from a low-level classification is an index into some // ShapeTable and a rating. struct ShapeRating { ShapeRating() : shape_id(0), rating(0.0f), raw(0.0f), font(0.0f), joined(false), broken(false) {} ShapeRating(int s, float r) : shape_id(s), rating(r), raw(1.0f), font(0.0f), joined(false), broken(false) {} // Sort function to sort ratings appropriately by descending rating. static int SortDescendingRating(const void* t1, const void* t2) { const ShapeRating* a = reinterpret_cast(t1); const ShapeRating* b = reinterpret_cast(t2); if (a->rating > b->rating) { return -1; } else if (a->rating < b->rating) { return 1; } else { return a->shape_id - b->shape_id; } } // Helper function to get the index of the first result with the required // unichar_id. If the results are sorted by rating, this will also be the // best result with the required unichar_id. // Returns -1 if the unichar_id is not found static int FirstResultWithUnichar(const GenericVector& results, const ShapeTable& shape_table, UNICHAR_ID unichar_id); // Index into some shape table indicates the class of the answer. int shape_id; // Rating from classifier with 1.0 perfect and 0.0 impossible. // Call it a probability if you must. float rating; // Subsidiary rating that a classifier may use internally. float raw; // Subsidiary rating that a classifier may use internally. float font; // Flag indicating that the input may be joined. bool joined; // Flag indicating that the input may be broken (a fragment). bool broken; }; // Simple struct to hold an entry for a heap-based priority queue of // ShapeRating. struct ShapeQueueEntry { ShapeQueueEntry() : result(ShapeRating(0, 0.0f)), level(0) {} ShapeQueueEntry(const ShapeRating& rating, int level0) : result(rating), level(level0) {} // Sort by decreasing rating and decreasing level for equal rating. bool operator<(const ShapeQueueEntry& other) const { if (result.rating > other.result.rating) return true; if (result.rating == other.result.rating) return level > other.level; return false; } // Output from classifier. ShapeRating result; // Which level in the tree did this come from? int level; }; typedef GenericHeap ShapeQueue; // Simple struct to hold a set of fonts associated with a single unichar-id. // A vector of UnicharAndFonts makes a shape. struct UnicharAndFonts { UnicharAndFonts() : unichar_id(0) { } UnicharAndFonts(int uni_id, int font_id) : unichar_id(uni_id) { font_ids.push_back(font_id); } // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); // Sort function to sort a pair of UnicharAndFonts by unichar_id. static int SortByUnicharId(const void* v1, const void* v2); GenericVector font_ids; inT32 unichar_id; }; // A Shape is a collection of unichar-ids and a list of fonts associated with // each, organized as a vector of UnicharAndFonts. Conceptually a Shape is // a classifiable unit, and represents a group of characters or parts of // characters that have a similar or identical shape. Shapes/ShapeTables may // be organized hierarchically from identical shapes at the leaves to vaguely // similar shapes near the root. class Shape { public: Shape() : destination_index_(-1) {} // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); int destination_index() const { return destination_index_; } void set_destination_index(int index) { destination_index_ = index; } int size() const { return unichars_.size(); } // Returns a UnicharAndFonts entry for the given index, which must be // in the range [0, size()). const UnicharAndFonts& operator[](int index) const { return unichars_[index]; } // Sets the unichar_id of the given index to the new unichar_id. void SetUnicharId(int index, int unichar_id) { unichars_[index].unichar_id = unichar_id; } // Adds a font_id for the given unichar_id. If the unichar_id is not // in the shape, it is added. void AddToShape(int unichar_id, int font_id); // Adds everything in other to this. void AddShape(const Shape& other); // Returns true if the shape contains the given unichar_id, font_id pair. bool ContainsUnicharAndFont(int unichar_id, int font_id) const; // Returns true if the shape contains the given unichar_id, ignoring font. bool ContainsUnichar(int unichar_id) const; // Returns true if the shape contains the given font, ignoring unichar_id. bool ContainsFont(int font_id) const; // Returns true if the shape contains the given font properties, ignoring // unichar_id. bool ContainsFontProperties(const FontInfoTable& font_table, uinT32 properties) const; // Returns true if the shape contains multiple different font properties, // ignoring unichar_id. bool ContainsMultipleFontProperties(const FontInfoTable& font_table) const; // Returns true if this shape is equal to other (ignoring order of unichars // and fonts). bool operator==(const Shape& other) const; // Returns true if this is a subset (including equal) of other. bool IsSubsetOf(const Shape& other) const; // Returns true if the lists of unichar ids are the same in this and other, // ignoring fonts. // NOT const, as it will sort the unichars on demand. bool IsEqualUnichars(Shape* other); private: // Sorts the unichars_ vector by unichar. void SortUnichars(); // Flag indicates that the unichars are sorted, allowing faster set // operations with another shape. bool unichars_sorted_; // If this Shape is part of a ShapeTable the destiation_index_ is the index // of some other shape in the ShapeTable with which this shape is merged. int destination_index_; // Array of unichars, each with a set of fonts. Each unichar has at most // one entry in the vector. GenericVector unichars_; }; // ShapeTable is a class to encapsulate the triple indirection that is // used here. // ShapeTable is a vector of shapes. // Each shape is a vector of UnicharAndFonts representing the set of unichars // that the shape represents. // Each UnicharAndFonts also lists the fonts of the unichar_id that were // mapped to the shape during training. class ShapeTable { public: ShapeTable(); // The UNICHARSET reference supplied here, or in set_unicharset below must // exist for the entire life of the ShapeTable. It is used only by DebugStr. explicit ShapeTable(const UNICHARSET& unicharset); // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); // Accessors. int NumShapes() const { return shape_table_.size(); } const UNICHARSET& unicharset() const { return *unicharset_; } // Returns the number of fonts used in this ShapeTable, computing it if // necessary. int NumFonts() const; // Shapetable takes a pointer to the UNICHARSET, so it must persist for the // entire life of the ShapeTable. void set_unicharset(const UNICHARSET& unicharset) { unicharset_ = &unicharset; } // Re-indexes the class_ids in the shapetable according to the given map. // Useful in conjunction with set_unicharset. void ReMapClassIds(const GenericVector& unicharset_map); // Returns a string listing the classes/fonts in a shape. STRING DebugStr(int shape_id) const; // Returns a debug string summarizing the table. STRING SummaryStr() const; // Adds a new shape starting with the given unichar_id and font_id. // Returns the assigned index. int AddShape(int unichar_id, int font_id); // Adds a copy of the given shape unless it is already present. // Returns the assigned index or index of existing shape if already present. int AddShape(const Shape& other); // Removes the shape given by the shape index. All indices above are changed! void DeleteShape(int shape_id); // Adds a font_id to the given existing shape index for the given // unichar_id. If the unichar_id is not in the shape, it is added. void AddToShape(int shape_id, int unichar_id, int font_id); // Adds the given shape to the existing shape with the given index. void AddShapeToShape(int shape_id, const Shape& other); // Returns the id of the shape that contains the given unichar and font. // If not found, returns -1. // If font_id < 0, the font_id is ignored and the first shape that matches // the unichar_id is returned. int FindShape(int unichar_id, int font_id) const; // Returns the first unichar_id and font_id in the given shape. void GetFirstUnicharAndFont(int shape_id, int* unichar_id, int* font_id) const; // Accessors for the Shape with the given shape_id. const Shape& GetShape(int shape_id) const { return *shape_table_[shape_id]; } Shape* MutableShape(int shape_id) { return shape_table_[shape_id]; } // Expands all the classes/fonts in the shape individually to build // a ShapeTable. int BuildFromShape(const Shape& shape, const ShapeTable& master_shapes); // Returns true if the shapes are already merged. bool AlreadyMerged(int shape_id1, int shape_id2) const; // Returns true if any shape contains multiple unichars. bool AnyMultipleUnichars() const; // Returns the maximum number of unichars over all shapes. int MaxNumUnichars() const; // Merges shapes with a common unichar over the [start, end) interval. // Assumes single unichar per shape. void ForceFontMerges(int start, int end); // Returns the number of unichars in the master shape. int MasterUnicharCount(int shape_id) const; // Returns the sum of the font counts in the master shape. int MasterFontCount(int shape_id) const; // Returns the number of unichars that would result from merging the shapes. int MergedUnicharCount(int shape_id1, int shape_id2) const; // Merges two shape_ids, leaving shape_id2 marked as merged. void MergeShapes(int shape_id1, int shape_id2); // Swaps two shape_ids. void SwapShapes(int shape_id1, int shape_id2); // Appends the master shapes from other to this. // Used to create a clean ShapeTable from a merged one, or to create a // copy of a ShapeTable. // If not NULL, shape_map is set to map other shape_ids to this's shape_ids. void AppendMasterShapes(const ShapeTable& other, GenericVector* shape_map); // Returns the number of master shapes remaining after merging. int NumMasterShapes() const; // Returns the destination of this shape, (if merged), taking into account // the fact that the destination may itself have been merged. // For a non-merged shape, returns the input shape_id. int MasterDestinationIndex(int shape_id) const; // Returns false if the unichars in neither shape is a subset of the other.. bool SubsetUnichar(int shape_id1, int shape_id2) const; // Returns false if the unichars in neither shape is a subset of the other.. bool MergeSubsetUnichar(int merge_id1, int merge_id2, int shape_id) const; // Returns true if the unichar sets are equal between the shapes. bool EqualUnichars(int shape_id1, int shape_id2) const; bool MergeEqualUnichars(int merge_id1, int merge_id2, int shape_id) const; // Returns true if there is a common unichar between the shapes. bool CommonUnichars(int shape_id1, int shape_id2) const; // Returns true if there is a common font id between the shapes. bool CommonFont(int shape_id1, int shape_id2) const; // Adds the unichars of the given shape_id to the vector of results. Any // unichar_id that is already present just has the fonts added to the // font set for that result without adding a new entry in the vector. // NOTE: it is assumed that the results are given to this function in order // of decreasing rating. // The unichar_map vector indicates the index of the results entry containing // each unichar, or -1 if the unichar is not yet included in results. void AddShapeToResults(const ShapeRating& shape_rating, GenericVector* unichar_map, GenericVector* results) const; private: // Adds the given unichar_id to the results if needed, updating unichar_map // and returning the index of unichar in results. int AddUnicharToResults(int unichar_id, float rating, GenericVector* unichar_map, GenericVector* results) const; // Pointer to a provided unicharset used only by the Debugstr member. const UNICHARSET* unicharset_; // Vector of pointers to the Shapes in this ShapeTable. PointerVector shape_table_; // Cached data calculated on demand. mutable int num_fonts_; }; } // namespace tesseract. #endif // TESSERACT_CLASSIFY_SHAPETABLE_H_ tesseract-3.04.01/classify/tessclassifier.cpp000066400000000000000000000071051266071204500212260ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: tessclassifier.cpp // Description: Tesseract implementation of a ShapeClassifier. // Author: Ray Smith // Created: Tue Nov 22 14:16:25 PST 2011 // // (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. // /////////////////////////////////////////////////////////////////////// #include "tessclassifier.h" #include "classify.h" #include "trainingsample.h" namespace tesseract { // Classifies the given [training] sample, writing to results. // See ShapeClassifier for a full description. int TessClassifier::UnicharClassifySample( const TrainingSample& sample, Pix* page_pix, int debug, UNICHAR_ID keep_this, GenericVector* results) { int old_matcher_level = classify_->matcher_debug_level; int old_matcher_flags = classify_->matcher_debug_flags; int old_classify_level = classify_->classify_debug_level; if (debug) { // Explicitly set values of various control parameters to generate debug // output if required, restoring the old values after classifying. classify_->matcher_debug_level.set_value(2); classify_->matcher_debug_flags.set_value(25); classify_->classify_debug_level.set_value(3); } classify_->CharNormTrainingSample(pruner_only_, keep_this, sample, results); if (debug) { classify_->matcher_debug_level.set_value(old_matcher_level); classify_->matcher_debug_flags.set_value(old_matcher_flags); classify_->classify_debug_level.set_value(old_classify_level); } return results->size(); } // Provides access to the ShapeTable that this classifier works with. const ShapeTable* TessClassifier::GetShapeTable() const { return classify_->shape_table(); } // Provides access to the UNICHARSET that this classifier works with. // Only needs to be overridden if GetShapeTable() can return NULL. const UNICHARSET& TessClassifier::GetUnicharset() const { return classify_->unicharset; } // Displays classification as the given shape_id. Creates as many windows // as it feels fit, using index as a guide for placement. Adds any created // windows to the windows output and returns a new index that may be used // by any subsequent classifiers. Caller waits for the user to view and // then destroys the windows by clearing the vector. int TessClassifier::DisplayClassifyAs( const TrainingSample& sample, Pix* page_pix, int unichar_id, int index, PointerVector* windows) { int shape_id = unichar_id; // TODO(rays) Fix this so it works with both flat and real shapetables. // if (GetShapeTable() != NULL) // shape_id = BestShapeForUnichar(sample, page_pix, unichar_id, NULL); if (shape_id < 0) return index; if (UnusedClassIdIn(classify_->PreTrainedTemplates, shape_id)) { tprintf("No built-in templates for class/shape %d\n", shape_id); return index; } classify_->ShowBestMatchFor(shape_id, sample.features(), sample.num_features()); return index; } } // namespace tesseract tesseract-3.04.01/classify/tessclassifier.h000066400000000000000000000061471266071204500207000ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) /////////////////////////////////////////////////////////////////////// // File: tessclassifier.h // Description: Tesseract implementation of a ShapeClassifier. // Author: Ray Smith // Created: Tue Nov 22 14:10:45 PST 2011 // // (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 THIRD_PARTY_TESSERACT_CLASSIFY_TESSCLASSIFIER_H_ #define THIRD_PARTY_TESSERACT_CLASSIFY_TESSCLASSIFIER_H_ #include "shapeclassifier.h" namespace tesseract { class Classify; class TrainingSample; // Tesseract implementation of a ShapeClassifier. // Due to limitations in the content of TrainingSample, this currently // only works for the static classifier and only works if the ShapeTable // in classify is not NULL. class TessClassifier : public ShapeClassifier { public: TessClassifier(bool pruner_only, tesseract::Classify* classify) : pruner_only_(pruner_only), classify_(classify) {} virtual ~TessClassifier() {} // Classifies the given [training] sample, writing to results. // See ShapeClassifier for a full description. virtual int UnicharClassifySample(const TrainingSample& sample, Pix* page_pix, int debug, UNICHAR_ID keep_this, GenericVector* results); // Provides access to the ShapeTable that this classifier works with. virtual const ShapeTable* GetShapeTable() const; // Provides access to the UNICHARSET that this classifier works with. // Only needs to be overridden if GetShapeTable() can return NULL. virtual const UNICHARSET& GetUnicharset() const; // Displays classification as the given shape_id. Creates as many windows // as it feels fit, using index as a guide for placement. Adds any created // windows to the windows output and returns a new index that may be used // by any subsequent classifiers. Caller waits for the user to view and // then destroys the windows by clearing the vector. virtual int DisplayClassifyAs(const TrainingSample& sample, Pix* page_pix, int unichar_id, int index, PointerVector* windows); private: // Indicates that this classifier is to use just the ClassPruner, or the // full classifier if false. bool pruner_only_; // Borrowed pointer to the actual Tesseract classifier. tesseract::Classify* classify_; }; } // namespace tesseract #endif /* THIRD_PARTY_TESSERACT_CLASSIFY_TESSCLASSIFIER_H_ */ tesseract-3.04.01/classify/trainingsample.cpp000066400000000000000000000332361266071204500212240ustar00rootroot00000000000000// Copyright 2010 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) // // 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 #include "trainingsample.h" #include #include "allheaders.h" #include "helpers.h" #include "intfeaturemap.h" #include "normfeat.h" #include "shapetable.h" namespace tesseract { ELISTIZE(TrainingSample) // Center of randomizing operations. const int kRandomizingCenter = 128; // Randomizing factors. const int TrainingSample::kYShiftValues[kSampleYShiftSize] = { 6, 3, -3, -6, 0 }; const double TrainingSample::kScaleValues[kSampleScaleSize] = { 1.0625, 0.9375, 1.0 }; TrainingSample::~TrainingSample() { delete [] features_; delete [] micro_features_; } // WARNING! Serialize/DeSerialize do not save/restore the "cache" data // members, which is mostly the mapped features, and the weight. // It is assumed these can all be reconstructed from what is saved. // Writes to the given file. Returns false in case of error. bool TrainingSample::Serialize(FILE* fp) const { if (fwrite(&class_id_, sizeof(class_id_), 1, fp) != 1) return false; if (fwrite(&font_id_, sizeof(font_id_), 1, fp) != 1) return false; if (fwrite(&page_num_, sizeof(page_num_), 1, fp) != 1) return false; if (!bounding_box_.Serialize(fp)) return false; if (fwrite(&num_features_, sizeof(num_features_), 1, fp) != 1) return false; if (fwrite(&num_micro_features_, sizeof(num_micro_features_), 1, fp) != 1) return false; if (fwrite(&outline_length_, sizeof(outline_length_), 1, fp) != 1) return false; if (static_cast(fwrite(features_, sizeof(*features_), num_features_, fp)) != num_features_) return false; if (static_cast(fwrite(micro_features_, sizeof(*micro_features_), num_micro_features_, fp)) != num_micro_features_) return false; if (fwrite(cn_feature_, sizeof(*cn_feature_), kNumCNParams, fp) != kNumCNParams) return false; if (fwrite(geo_feature_, sizeof(*geo_feature_), GeoCount, fp) != GeoCount) return false; return true; } // Creates from the given file. Returns NULL in case of error. // If swap is true, assumes a big/little-endian swap is needed. TrainingSample* TrainingSample::DeSerializeCreate(bool swap, FILE* fp) { TrainingSample* sample = new TrainingSample; if (sample->DeSerialize(swap, fp)) return sample; delete sample; return NULL; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool TrainingSample::DeSerialize(bool swap, FILE* fp) { if (fread(&class_id_, sizeof(class_id_), 1, fp) != 1) return false; if (fread(&font_id_, sizeof(font_id_), 1, fp) != 1) return false; if (fread(&page_num_, sizeof(page_num_), 1, fp) != 1) return false; if (!bounding_box_.DeSerialize(swap, fp)) return false; if (fread(&num_features_, sizeof(num_features_), 1, fp) != 1) return false; if (fread(&num_micro_features_, sizeof(num_micro_features_), 1, fp) != 1) return false; if (fread(&outline_length_, sizeof(outline_length_), 1, fp) != 1) return false; if (swap) { ReverseN(&class_id_, sizeof(class_id_)); ReverseN(&num_features_, sizeof(num_features_)); ReverseN(&num_micro_features_, sizeof(num_micro_features_)); ReverseN(&outline_length_, sizeof(outline_length_)); } delete [] features_; features_ = new INT_FEATURE_STRUCT[num_features_]; if (static_cast(fread(features_, sizeof(*features_), num_features_, fp)) != num_features_) return false; delete [] micro_features_; micro_features_ = new MicroFeature[num_micro_features_]; if (static_cast(fread(micro_features_, sizeof(*micro_features_), num_micro_features_, fp)) != num_micro_features_) return false; if (fread(cn_feature_, sizeof(*cn_feature_), kNumCNParams, fp) != kNumCNParams) return false; if (fread(geo_feature_, sizeof(*geo_feature_), GeoCount, fp) != GeoCount) return false; return true; } // Saves the given features into a TrainingSample. TrainingSample* TrainingSample::CopyFromFeatures( const INT_FX_RESULT_STRUCT& fx_info, const TBOX& bounding_box, const INT_FEATURE_STRUCT* features, int num_features) { TrainingSample* sample = new TrainingSample; sample->num_features_ = num_features; sample->features_ = new INT_FEATURE_STRUCT[num_features]; sample->outline_length_ = fx_info.Length; memcpy(sample->features_, features, num_features * sizeof(features[0])); sample->geo_feature_[GeoBottom] = bounding_box.bottom(); sample->geo_feature_[GeoTop] = bounding_box.top(); sample->geo_feature_[GeoWidth] = bounding_box.width(); // Generate the cn_feature_ from the fx_info. sample->cn_feature_[CharNormY] = MF_SCALE_FACTOR * (fx_info.Ymean - kBlnBaselineOffset); sample->cn_feature_[CharNormLength] = MF_SCALE_FACTOR * fx_info.Length / LENGTH_COMPRESSION; sample->cn_feature_[CharNormRx] = MF_SCALE_FACTOR * fx_info.Rx; sample->cn_feature_[CharNormRy] = MF_SCALE_FACTOR * fx_info.Ry; sample->features_are_indexed_ = false; sample->features_are_mapped_ = false; return sample; } // Returns the cn_feature as a FEATURE_STRUCT* needed by cntraining. FEATURE_STRUCT* TrainingSample::GetCNFeature() const { FEATURE feature = NewFeature(&CharNormDesc); for (int i = 0; i < kNumCNParams; ++i) feature->Params[i] = cn_feature_[i]; return feature; } // Constructs and returns a copy randomized by the method given by // the randomizer index. If index is out of [0, kSampleRandomSize) then // an exact copy is returned. TrainingSample* TrainingSample::RandomizedCopy(int index) const { TrainingSample* sample = Copy(); if (index >= 0 && index < kSampleRandomSize) { ++index; // Remove the first combination. int yshift = kYShiftValues[index / kSampleScaleSize]; double scaling = kScaleValues[index % kSampleScaleSize]; for (int i = 0; i < num_features_; ++i) { double result = (features_[i].X - kRandomizingCenter) * scaling; result += kRandomizingCenter; sample->features_[i].X = ClipToRange(static_cast(result + 0.5), 0, MAX_UINT8); result = (features_[i].Y - kRandomizingCenter) * scaling; result += kRandomizingCenter + yshift; sample->features_[i].Y = ClipToRange(static_cast(result + 0.5), 0, MAX_UINT8); } } return sample; } // Constructs and returns an exact copy. TrainingSample* TrainingSample::Copy() const { TrainingSample* sample = new TrainingSample; sample->class_id_ = class_id_; sample->font_id_ = font_id_; sample->weight_ = weight_; sample->sample_index_ = sample_index_; sample->num_features_ = num_features_; if (num_features_ > 0) { sample->features_ = new INT_FEATURE_STRUCT[num_features_]; memcpy(sample->features_, features_, num_features_ * sizeof(features_[0])); } sample->num_micro_features_ = num_micro_features_; if (num_micro_features_ > 0) { sample->micro_features_ = new MicroFeature[num_micro_features_]; memcpy(sample->micro_features_, micro_features_, num_micro_features_ * sizeof(micro_features_[0])); } memcpy(sample->cn_feature_, cn_feature_, sizeof(*cn_feature_) * kNumCNParams); memcpy(sample->geo_feature_, geo_feature_, sizeof(*geo_feature_) * GeoCount); return sample; } // Extracts the needed information from the CHAR_DESC_STRUCT. void TrainingSample::ExtractCharDesc(int int_feature_type, int micro_type, int cn_type, int geo_type, CHAR_DESC_STRUCT* char_desc) { // Extract the INT features. if (features_ != NULL) delete [] features_; FEATURE_SET_STRUCT* char_features = char_desc->FeatureSets[int_feature_type]; if (char_features == NULL) { tprintf("Error: no features to train on of type %s\n", kIntFeatureType); num_features_ = 0; features_ = NULL; } else { num_features_ = char_features->NumFeatures; features_ = new INT_FEATURE_STRUCT[num_features_]; for (int f = 0; f < num_features_; ++f) { features_[f].X = static_cast(char_features->Features[f]->Params[IntX]); features_[f].Y = static_cast(char_features->Features[f]->Params[IntY]); features_[f].Theta = static_cast(char_features->Features[f]->Params[IntDir]); features_[f].CP_misses = 0; } } // Extract the Micro features. if (micro_features_ != NULL) delete [] micro_features_; char_features = char_desc->FeatureSets[micro_type]; if (char_features == NULL) { tprintf("Error: no features to train on of type %s\n", kMicroFeatureType); num_micro_features_ = 0; micro_features_ = NULL; } else { num_micro_features_ = char_features->NumFeatures; micro_features_ = new MicroFeature[num_micro_features_]; for (int f = 0; f < num_micro_features_; ++f) { for (int d = 0; d < MFCount; ++d) { micro_features_[f][d] = char_features->Features[f]->Params[d]; } } } // Extract the CN feature. char_features = char_desc->FeatureSets[cn_type]; if (char_features == NULL) { tprintf("Error: no CN feature to train on.\n"); } else { ASSERT_HOST(char_features->NumFeatures == 1); cn_feature_[CharNormY] = char_features->Features[0]->Params[CharNormY]; cn_feature_[CharNormLength] = char_features->Features[0]->Params[CharNormLength]; cn_feature_[CharNormRx] = char_features->Features[0]->Params[CharNormRx]; cn_feature_[CharNormRy] = char_features->Features[0]->Params[CharNormRy]; } // Extract the Geo feature. char_features = char_desc->FeatureSets[geo_type]; if (char_features == NULL) { tprintf("Error: no Geo feature to train on.\n"); } else { ASSERT_HOST(char_features->NumFeatures == 1); geo_feature_[GeoBottom] = char_features->Features[0]->Params[GeoBottom]; geo_feature_[GeoTop] = char_features->Features[0]->Params[GeoTop]; geo_feature_[GeoWidth] = char_features->Features[0]->Params[GeoWidth]; } features_are_indexed_ = false; features_are_mapped_ = false; } // Sets the mapped_features_ from the features_ using the provided // feature_space to the indexed versions of the features. void TrainingSample::IndexFeatures(const IntFeatureSpace& feature_space) { GenericVector indexed_features; feature_space.IndexAndSortFeatures(features_, num_features_, &mapped_features_); features_are_indexed_ = true; features_are_mapped_ = false; } // Sets the mapped_features_ from the features using the provided // feature_map. void TrainingSample::MapFeatures(const IntFeatureMap& feature_map) { GenericVector indexed_features; feature_map.feature_space().IndexAndSortFeatures(features_, num_features_, &indexed_features); feature_map.MapIndexedFeatures(indexed_features, &mapped_features_); features_are_indexed_ = false; features_are_mapped_ = true; } // Returns a pix representing the sample. (Int features only.) Pix* TrainingSample::RenderToPix(const UNICHARSET* unicharset) const { Pix* pix = pixCreate(kIntFeatureExtent, kIntFeatureExtent, 1); for (int f = 0; f < num_features_; ++f) { int start_x = features_[f].X; int start_y = kIntFeatureExtent - features_[f].Y; double dx = cos((features_[f].Theta / 256.0) * 2.0 * PI - PI); double dy = -sin((features_[f].Theta / 256.0) * 2.0 * PI - PI); for (int i = 0; i <= 5; ++i) { int x = static_cast(start_x + dx * i); int y = static_cast(start_y + dy * i); if (x >= 0 && x < 256 && y >= 0 && y < 256) pixSetPixel(pix, x, y, 1); } } if (unicharset != NULL) pixSetText(pix, unicharset->id_to_unichar(class_id_)); return pix; } // Displays the features in the given window with the given color. void TrainingSample::DisplayFeatures(ScrollView::Color color, ScrollView* window) const { #ifndef GRAPHICS_DISABLED for (int f = 0; f < num_features_; ++f) { RenderIntFeature(window, &features_[f], color); } #endif // GRAPHICS_DISABLED } // Returns a pix of the original sample image. The pix is padded all round // by padding wherever possible. // The returned Pix must be pixDestroyed after use. // If the input page_pix is NULL, NULL is returned. Pix* TrainingSample::GetSamplePix(int padding, Pix* page_pix) const { if (page_pix == NULL) return NULL; int page_width = pixGetWidth(page_pix); int page_height = pixGetHeight(page_pix); TBOX padded_box = bounding_box(); padded_box.pad(padding, padding); // Clip the padded_box to the limits of the page TBOX page_box(0, 0, page_width, page_height); padded_box &= page_box; Box* box = boxCreate(page_box.left(), page_height - page_box.top(), page_box.width(), page_box.height()); Pix* sample_pix = pixClipRectangle(page_pix, box, NULL); boxDestroy(&box); return sample_pix; } } // namespace tesseract tesseract-3.04.01/classify/trainingsample.h000066400000000000000000000206441266071204500206700ustar00rootroot00000000000000// Copyright 2010 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) // // 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_TRAININGSAMPLE_H__ #define TESSERACT_TRAINING_TRAININGSAMPLE_H__ #include "elst.h" #include "featdefs.h" #include "intfx.h" #include "intmatcher.h" #include "matrix.h" #include "mf.h" #include "picofeat.h" #include "shapetable.h" #include "unicharset.h" struct Pix; namespace tesseract { class IntFeatureMap; class IntFeatureSpace; class ShapeTable; // Number of elements of cn_feature_. static const int kNumCNParams = 4; // Number of ways to shift the features when randomizing. static const int kSampleYShiftSize = 5; // Number of ways to scale the features when randomizing. static const int kSampleScaleSize = 3; // Total number of different ways to manipulate the features when randomizing. // The first and last combinations are removed to avoid an excessive // top movement (first) and an identity transformation (last). // WARNING: To avoid patterned duplication of samples, be sure to keep // kSampleRandomSize prime! // Eg with current values (kSampleYShiftSize = 5 and TkSampleScaleSize = 3) // kSampleRandomSize is 13, which is prime. static const int kSampleRandomSize = kSampleYShiftSize * kSampleScaleSize - 2; // ASSERT_IS_PRIME(kSampleRandomSize) !! class TrainingSample : public ELIST_LINK { public: TrainingSample() : class_id_(INVALID_UNICHAR_ID), font_id_(0), page_num_(0), num_features_(0), num_micro_features_(0), outline_length_(0), features_(NULL), micro_features_(NULL), weight_(1.0), max_dist_(0.0), sample_index_(0), features_are_indexed_(false), features_are_mapped_(false), is_error_(false) { } ~TrainingSample(); // Saves the given features into a TrainingSample. The features are copied, // so may be deleted afterwards. Delete the return value after use. static TrainingSample* CopyFromFeatures(const INT_FX_RESULT_STRUCT& fx_info, const TBOX& bounding_box, const INT_FEATURE_STRUCT* features, int num_features); // Returns the cn_feature as a FEATURE_STRUCT* needed by cntraining. FEATURE_STRUCT* GetCNFeature() const; // Constructs and returns a copy "randomized" by the method given by // the randomizer index. If index is out of [0, kSampleRandomSize) then // an exact copy is returned. TrainingSample* RandomizedCopy(int index) const; // Constructs and returns an exact copy. TrainingSample* Copy() const; // WARNING! Serialize/DeSerialize do not save/restore the "cache" data // members, which is mostly the mapped features, and the weight. // It is assumed these can all be reconstructed from what is saved. // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Creates from the given file. Returns NULL in case of error. // If swap is true, assumes a big/little-endian swap is needed. static TrainingSample* DeSerializeCreate(bool swap, FILE* fp); // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); // Extracts the needed information from the CHAR_DESC_STRUCT. void ExtractCharDesc(int feature_type, int micro_type, int cn_type, int geo_type, CHAR_DESC_STRUCT* char_desc); // Sets the mapped_features_ from the features_ using the provided // feature_space to the indexed versions of the features. void IndexFeatures(const IntFeatureSpace& feature_space); // Sets the mapped_features_ from the features_ using the provided // feature_map. void MapFeatures(const IntFeatureMap& feature_map); // Returns a pix representing the sample. (Int features only.) Pix* RenderToPix(const UNICHARSET* unicharset) const; // Displays the features in the given window with the given color. void DisplayFeatures(ScrollView::Color color, ScrollView* window) const; // Returns a pix of the original sample image. The pix is padded all round // by padding wherever possible. // The returned Pix must be pixDestroyed after use. // If the input page_pix is NULL, NULL is returned. Pix* GetSamplePix(int padding, Pix* page_pix) const; // Accessors. UNICHAR_ID class_id() const { return class_id_; } void set_class_id(int id) { class_id_ = id; } int font_id() const { return font_id_; } void set_font_id(int id) { font_id_ = id; } int page_num() const { return page_num_; } void set_page_num(int page) { page_num_ = page; } const TBOX& bounding_box() const { return bounding_box_; } void set_bounding_box(const TBOX& box) { bounding_box_ = box; } int num_features() const { return num_features_; } const INT_FEATURE_STRUCT* features() const { return features_; } int num_micro_features() const { return num_micro_features_; } const MicroFeature* micro_features() const { return micro_features_; } int outline_length() const { return outline_length_; } float cn_feature(int index) const { return cn_feature_[index]; } int geo_feature(int index) const { return geo_feature_[index]; } double weight() const { return weight_; } void set_weight(double value) { weight_ = value; } double max_dist() const { return max_dist_; } void set_max_dist(double value) { max_dist_ = value; } int sample_index() const { return sample_index_; } void set_sample_index(int value) { sample_index_ = value; } bool features_are_mapped() const { return features_are_mapped_; } const GenericVector& mapped_features() const { ASSERT_HOST(features_are_mapped_); return mapped_features_; } const GenericVector& indexed_features() const { ASSERT_HOST(features_are_indexed_); return mapped_features_; } bool is_error() const { return is_error_; } void set_is_error(bool value) { is_error_ = value; } private: // Unichar id that this sample represents. There obviously must be a // reference UNICHARSET somewhere. Usually in TrainingSampleSet. UNICHAR_ID class_id_; // Font id in which this sample was printed. Refers to a fontinfo_table_ in // MasterTrainer. int font_id_; // Number of page that the sample came from. int page_num_; // Bounding box of sample in original image. TBOX bounding_box_; // Number of INT_FEATURE_STRUCT in features_ array. int num_features_; // Number of MicroFeature in micro_features_ array. int num_micro_features_; // Total length of outline in the baseline normalized coordinate space. // See comment in WERD_RES class definition for a discussion of coordinate // spaces. int outline_length_; // Array of features. INT_FEATURE_STRUCT* features_; // Array of features. MicroFeature* micro_features_; // The one and only CN feature. Indexed by NORM_PARAM_NAME enum. float cn_feature_[kNumCNParams]; // The one and only geometric feature. (Aims at replacing cn_feature_). // Indexed by GeoParams enum in picofeat.h int geo_feature_[GeoCount]; // Non-serialized cache data. // Weight used for boosting training. double weight_; // Maximum distance to other samples of same class/font used in computing // the canonical sample. double max_dist_; // Global index of this sample. int sample_index_; // Indexed/mapped features, as indicated by the bools below. GenericVector mapped_features_; bool features_are_indexed_; bool features_are_mapped_; // True if the last classification was an error by the current definition. bool is_error_; // Randomizing factors. static const int kYShiftValues[kSampleYShiftSize]; static const double kScaleValues[kSampleScaleSize]; }; ELISTIZEH(TrainingSample) } // namespace tesseract #endif // TESSERACT_TRAINING_TRAININGSAMPLE_H__ tesseract-3.04.01/classify/trainingsampleset.cpp000066400000000000000000001060001266071204500217260ustar00rootroot00000000000000// Copyright 2010 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) // // 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 "trainingsampleset.h" #include "allheaders.h" #include "boxread.h" #include "fontinfo.h" #include "indexmapbidi.h" #include "intfeaturedist.h" #include "intfeaturemap.h" #include "intfeaturespace.h" #include "shapetable.h" #include "trainingsample.h" #include "unicity_table.h" namespace tesseract { const int kTestChar = -1; // 37; // Max number of distances to compute the squared way const int kSquareLimit = 25; // Prime numbers for subsampling distances. const int kPrime1 = 17; const int kPrime2 = 13; // Min samples from which to start discarding outliers. const int kMinOutlierSamples = 5; TrainingSampleSet::FontClassInfo::FontClassInfo() : num_raw_samples(0), canonical_sample(-1), canonical_dist(0.0f) { } // Writes to the given file. Returns false in case of error. bool TrainingSampleSet::FontClassInfo::Serialize(FILE* fp) const { if (fwrite(&num_raw_samples, sizeof(num_raw_samples), 1, fp) != 1) return false; if (fwrite(&canonical_sample, sizeof(canonical_sample), 1, fp) != 1) return false; if (fwrite(&canonical_dist, sizeof(canonical_dist), 1, fp) != 1) return false; if (!samples.Serialize(fp)) return false; return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool TrainingSampleSet::FontClassInfo::DeSerialize(bool swap, FILE* fp) { if (fread(&num_raw_samples, sizeof(num_raw_samples), 1, fp) != 1) return false; if (fread(&canonical_sample, sizeof(canonical_sample), 1, fp) != 1) return false; if (fread(&canonical_dist, sizeof(canonical_dist), 1, fp) != 1) return false; if (!samples.DeSerialize(swap, fp)) return false; if (swap) { ReverseN(&num_raw_samples, sizeof(num_raw_samples)); ReverseN(&canonical_sample, sizeof(canonical_sample)); ReverseN(&canonical_dist, sizeof(canonical_dist)); } return true; } TrainingSampleSet::TrainingSampleSet(const FontInfoTable& font_table) : num_raw_samples_(0), unicharset_size_(0), font_class_array_(NULL), fontinfo_table_(font_table) { } TrainingSampleSet::~TrainingSampleSet() { delete font_class_array_; } // Writes to the given file. Returns false in case of error. bool TrainingSampleSet::Serialize(FILE* fp) const { if (!samples_.Serialize(fp)) return false; if (!unicharset_.save_to_file(fp)) return false; if (!font_id_map_.Serialize(fp)) return false; inT8 not_null = font_class_array_ != NULL; if (fwrite(¬_null, sizeof(not_null), 1, fp) != 1) return false; if (not_null) { if (!font_class_array_->SerializeClasses(fp)) return false; } return true; } // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool TrainingSampleSet::DeSerialize(bool swap, FILE* fp) { if (!samples_.DeSerialize(swap, fp)) return false; num_raw_samples_ = samples_.size(); if (!unicharset_.load_from_file(fp)) return false; if (!font_id_map_.DeSerialize(swap, fp)) return false; if (font_class_array_ != NULL) { delete font_class_array_; font_class_array_ = NULL; } inT8 not_null; if (fread(¬_null, sizeof(not_null), 1, fp) != 1) return false; if (not_null) { FontClassInfo empty; font_class_array_ = new GENERIC_2D_ARRAY(1, 1 , empty); if (!font_class_array_->DeSerializeClasses(swap, fp)) return false; } unicharset_size_ = unicharset_.size(); return true; } // Load an initial unicharset, or set one up if the file cannot be read. void TrainingSampleSet::LoadUnicharset(const char* filename) { if (!unicharset_.load_from_file(filename)) { tprintf("Failed to load unicharset from file %s\n" "Building unicharset from scratch...\n", filename); unicharset_.clear(); // Add special characters as they were removed by the clear. UNICHARSET empty; unicharset_.AppendOtherUnicharset(empty); } unicharset_size_ = unicharset_.size(); } // Adds a character sample to this sample set. // If the unichar is not already in the local unicharset, it is added. // Returns the unichar_id of the added sample, from the local unicharset. int TrainingSampleSet::AddSample(const char* unichar, TrainingSample* sample) { if (!unicharset_.contains_unichar(unichar)) { unicharset_.unichar_insert(unichar); if (unicharset_.size() > MAX_NUM_CLASSES) { tprintf("Error: Size of unicharset in TrainingSampleSet::AddSample is " "greater than MAX_NUM_CLASSES\n"); return -1; } } UNICHAR_ID char_id = unicharset_.unichar_to_id(unichar); AddSample(char_id, sample); return char_id; } // Adds a character sample to this sample set with the given unichar_id, // which must correspond to the local unicharset (in this). void TrainingSampleSet::AddSample(int unichar_id, TrainingSample* sample) { sample->set_class_id(unichar_id); samples_.push_back(sample); num_raw_samples_ = samples_.size(); unicharset_size_ = unicharset_.size(); } // Returns the number of samples for the given font,class pair. // If randomize is true, returns the number of samples accessible // with randomizing on. (Increases the number of samples if small.) // OrganizeByFontAndClass must have been already called. int TrainingSampleSet::NumClassSamples(int font_id, int class_id, bool randomize) const { ASSERT_HOST(font_class_array_ != NULL); if (font_id < 0 || class_id < 0 || font_id >= font_id_map_.SparseSize() || class_id >= unicharset_size_) { // There are no samples because the font or class doesn't exist. return 0; } int font_index = font_id_map_.SparseToCompact(font_id); if (font_index < 0) return 0; // The font has no samples. if (randomize) return (*font_class_array_)(font_index, class_id).samples.size(); else return (*font_class_array_)(font_index, class_id).num_raw_samples; } // Gets a sample by its index. const TrainingSample* TrainingSampleSet::GetSample(int index) const { return samples_[index]; } // Gets a sample by its font, class, index. // OrganizeByFontAndClass must have been already called. const TrainingSample* TrainingSampleSet::GetSample(int font_id, int class_id, int index) const { ASSERT_HOST(font_class_array_ != NULL); int font_index = font_id_map_.SparseToCompact(font_id); if (font_index < 0) return NULL; int sample_index = (*font_class_array_)(font_index, class_id).samples[index]; return samples_[sample_index]; } // Get a sample by its font, class, index. Does not randomize. // OrganizeByFontAndClass must have been already called. TrainingSample* TrainingSampleSet::MutableSample(int font_id, int class_id, int index) { ASSERT_HOST(font_class_array_ != NULL); int font_index = font_id_map_.SparseToCompact(font_id); if (font_index < 0) return NULL; int sample_index = (*font_class_array_)(font_index, class_id).samples[index]; return samples_[sample_index]; } // Returns a string debug representation of the given sample: // font, unichar_str, bounding box, page. STRING TrainingSampleSet::SampleToString(const TrainingSample& sample) const { STRING boxfile_str; MakeBoxFileStr(unicharset_.id_to_unichar(sample.class_id()), sample.bounding_box(), sample.page_num(), &boxfile_str); return STRING(fontinfo_table_.get(sample.font_id()).name) + " " + boxfile_str; } // Gets the combined set of features used by all the samples of the given // font/class combination. const BitVector& TrainingSampleSet::GetCloudFeatures( int font_id, int class_id) const { int font_index = font_id_map_.SparseToCompact(font_id); ASSERT_HOST(font_index >= 0); return (*font_class_array_)(font_index, class_id).cloud_features; } // Gets the indexed features of the canonical sample of the given // font/class combination. const GenericVector& TrainingSampleSet::GetCanonicalFeatures( int font_id, int class_id) const { int font_index = font_id_map_.SparseToCompact(font_id); ASSERT_HOST(font_index >= 0); return (*font_class_array_)(font_index, class_id).canonical_features; } // Returns the distance between the given UniCharAndFonts pair. // If matched_fonts, only matching fonts, are considered, unless that yields // the empty set. // OrganizeByFontAndClass must have been already called. float TrainingSampleSet::UnicharDistance(const UnicharAndFonts& uf1, const UnicharAndFonts& uf2, bool matched_fonts, const IntFeatureMap& feature_map) { int num_fonts1 = uf1.font_ids.size(); int c1 = uf1.unichar_id; int num_fonts2 = uf2.font_ids.size(); int c2 = uf2.unichar_id; double dist_sum = 0.0; int dist_count = 0; bool debug = false; if (matched_fonts) { // Compute distances only where fonts match. for (int i = 0; i < num_fonts1; ++i) { int f1 = uf1.font_ids[i]; for (int j = 0; j < num_fonts2; ++j) { int f2 = uf2.font_ids[j]; if (f1 == f2) { dist_sum += ClusterDistance(f1, c1, f2, c2, feature_map); ++dist_count; } } } } else if (num_fonts1 * num_fonts2 <= kSquareLimit) { // Small enough sets to compute all the distances. for (int i = 0; i < num_fonts1; ++i) { int f1 = uf1.font_ids[i]; for (int j = 0; j < num_fonts2; ++j) { int f2 = uf2.font_ids[j]; dist_sum += ClusterDistance(f1, c1, f2, c2, feature_map); if (debug) { tprintf("Cluster dist %d %d %d %d = %g\n", f1, c1, f2, c2, ClusterDistance(f1, c1, f2, c2, feature_map)); } ++dist_count; } } } else { // Subsample distances, using the largest set once, and stepping through // the smaller set so as to ensure that all the pairs are different. int increment = kPrime1 != num_fonts2 ? kPrime1 : kPrime2; int index = 0; int num_samples = MAX(num_fonts1, num_fonts2); for (int i = 0; i < num_samples; ++i, index += increment) { int f1 = uf1.font_ids[i % num_fonts1]; int f2 = uf2.font_ids[index % num_fonts2]; if (debug) { tprintf("Cluster dist %d %d %d %d = %g\n", f1, c1, f2, c2, ClusterDistance(f1, c1, f2, c2, feature_map)); } dist_sum += ClusterDistance(f1, c1, f2, c2, feature_map); ++dist_count; } } if (dist_count == 0) { if (matched_fonts) return UnicharDistance(uf1, uf2, false, feature_map); return 0.0f; } return dist_sum / dist_count; } // Returns the distance between the given pair of font/class pairs. // Finds in cache or computes and caches. // OrganizeByFontAndClass must have been already called. float TrainingSampleSet::ClusterDistance(int font_id1, int class_id1, int font_id2, int class_id2, const IntFeatureMap& feature_map) { ASSERT_HOST(font_class_array_ != NULL); int font_index1 = font_id_map_.SparseToCompact(font_id1); int font_index2 = font_id_map_.SparseToCompact(font_id2); if (font_index1 < 0 || font_index2 < 0) return 0.0f; FontClassInfo& fc_info = (*font_class_array_)(font_index1, class_id1); if (font_id1 == font_id2) { // Special case cache for speed. if (fc_info.unichar_distance_cache.size() == 0) fc_info.unichar_distance_cache.init_to_size(unicharset_size_, -1.0f); if (fc_info.unichar_distance_cache[class_id2] < 0) { // Distance has to be calculated. float result = ComputeClusterDistance(font_id1, class_id1, font_id2, class_id2, feature_map); fc_info.unichar_distance_cache[class_id2] = result; // Copy to the symmetric cache entry. FontClassInfo& fc_info2 = (*font_class_array_)(font_index2, class_id2); if (fc_info2.unichar_distance_cache.size() == 0) fc_info2.unichar_distance_cache.init_to_size(unicharset_size_, -1.0f); fc_info2.unichar_distance_cache[class_id1] = result; } return fc_info.unichar_distance_cache[class_id2]; } else if (class_id1 == class_id2) { // Another special-case cache for equal class-id. if (fc_info.font_distance_cache.size() == 0) fc_info.font_distance_cache.init_to_size(font_id_map_.CompactSize(), -1.0f); if (fc_info.font_distance_cache[font_index2] < 0) { // Distance has to be calculated. float result = ComputeClusterDistance(font_id1, class_id1, font_id2, class_id2, feature_map); fc_info.font_distance_cache[font_index2] = result; // Copy to the symmetric cache entry. FontClassInfo& fc_info2 = (*font_class_array_)(font_index2, class_id2); if (fc_info2.font_distance_cache.size() == 0) fc_info2.font_distance_cache.init_to_size(font_id_map_.CompactSize(), -1.0f); fc_info2.font_distance_cache[font_index1] = result; } return fc_info.font_distance_cache[font_index2]; } // Both font and class are different. Linear search for class_id2/font_id2 // in what is a hopefully short list of distances. int cache_index = 0; while (cache_index < fc_info.distance_cache.size() && (fc_info.distance_cache[cache_index].unichar_id != class_id2 || fc_info.distance_cache[cache_index].font_id != font_id2)) ++cache_index; if (cache_index == fc_info.distance_cache.size()) { // Distance has to be calculated. float result = ComputeClusterDistance(font_id1, class_id1, font_id2, class_id2, feature_map); FontClassDistance fc_dist = { class_id2, font_id2, result }; fc_info.distance_cache.push_back(fc_dist); // Copy to the symmetric cache entry. We know it isn't there already, as // we always copy to the symmetric entry. FontClassInfo& fc_info2 = (*font_class_array_)(font_index2, class_id2); fc_dist.unichar_id = class_id1; fc_dist.font_id = font_id1; fc_info2.distance_cache.push_back(fc_dist); } return fc_info.distance_cache[cache_index].distance; } // Computes the distance between the given pair of font/class pairs. float TrainingSampleSet::ComputeClusterDistance( int font_id1, int class_id1, int font_id2, int class_id2, const IntFeatureMap& feature_map) const { int dist = ReliablySeparable(font_id1, class_id1, font_id2, class_id2, feature_map, false); dist += ReliablySeparable(font_id2, class_id2, font_id1, class_id1, feature_map, false); int denominator = GetCanonicalFeatures(font_id1, class_id1).size(); denominator += GetCanonicalFeatures(font_id2, class_id2).size(); return static_cast(dist) / denominator; } // Helper to add a feature and its near neighbors to the good_features. // levels indicates how many times to compute the offset features of what is // already there. This is done by iteration rather than recursion. static void AddNearFeatures(const IntFeatureMap& feature_map, int f, int levels, GenericVector* good_features) { int prev_num_features = 0; good_features->push_back(f); int num_features = 1; for (int level = 0; level < levels; ++level) { for (int i = prev_num_features; i < num_features; ++i) { int feature = (*good_features)[i]; for (int dir = -kNumOffsetMaps; dir <= kNumOffsetMaps; ++dir) { if (dir == 0) continue; int f1 = feature_map.OffsetFeature(feature, dir); if (f1 >= 0) { good_features->push_back(f1); } } } prev_num_features = num_features; num_features = good_features->size(); } } // Returns the number of canonical features of font/class 2 for which // neither the feature nor any of its near neighbors occurs in the cloud // of font/class 1. Each such feature is a reliable separation between // the classes, ASSUMING that the canonical sample is sufficiently // representative that every sample has a feature near that particular // feature. To check that this is so on the fly would be prohibitively // expensive, but it might be possible to pre-qualify the canonical features // to include only those for which this assumption is true. // ComputeCanonicalFeatures and ComputeCloudFeatures must have been called // first, or the results will be nonsense. int TrainingSampleSet::ReliablySeparable(int font_id1, int class_id1, int font_id2, int class_id2, const IntFeatureMap& feature_map, bool thorough) const { int result = 0; const TrainingSample* sample2 = GetCanonicalSample(font_id2, class_id2); if (sample2 == NULL) return 0; // There are no canonical features. const GenericVector& canonical2 = GetCanonicalFeatures(font_id2, class_id2); const BitVector& cloud1 = GetCloudFeatures(font_id1, class_id1); if (cloud1.size() == 0) return canonical2.size(); // There are no cloud features. // Find a canonical2 feature that is not in cloud1. for (int f = 0; f < canonical2.size(); ++f) { int feature = canonical2[f]; if (cloud1[feature]) continue; // Gather the near neighbours of f. GenericVector good_features; AddNearFeatures(feature_map, feature, 1, &good_features); // Check that none of the good_features are in the cloud. int i; for (i = 0; i < good_features.size(); ++i) { int good_f = good_features[i]; if (cloud1[good_f]) { break; } } if (i < good_features.size()) continue; // Found one in the cloud. ++result; } return result; } // Returns the total index of the requested sample. // OrganizeByFontAndClass must have been already called. int TrainingSampleSet::GlobalSampleIndex(int font_id, int class_id, int index) const { ASSERT_HOST(font_class_array_ != NULL); int font_index = font_id_map_.SparseToCompact(font_id); if (font_index < 0) return -1; return (*font_class_array_)(font_index, class_id).samples[index]; } // Gets the canonical sample for the given font, class pair. // ComputeCanonicalSamples must have been called first. const TrainingSample* TrainingSampleSet::GetCanonicalSample( int font_id, int class_id) const { ASSERT_HOST(font_class_array_ != NULL); int font_index = font_id_map_.SparseToCompact(font_id); if (font_index < 0) return NULL; int sample_index = (*font_class_array_)(font_index, class_id).canonical_sample; return sample_index >= 0 ? samples_[sample_index] : NULL; } // Gets the max distance for the given canonical sample. // ComputeCanonicalSamples must have been called first. float TrainingSampleSet::GetCanonicalDist(int font_id, int class_id) const { ASSERT_HOST(font_class_array_ != NULL); int font_index = font_id_map_.SparseToCompact(font_id); if (font_index < 0) return 0.0f; if ((*font_class_array_)(font_index, class_id).canonical_sample >= 0) return (*font_class_array_)(font_index, class_id).canonical_dist; else return 0.0f; } // Generates indexed features for all samples with the supplied feature_space. void TrainingSampleSet::IndexFeatures(const IntFeatureSpace& feature_space) { for (int s = 0; s < samples_.size(); ++s) samples_[s]->IndexFeatures(feature_space); } // Delete outlier samples with few features that are shared with others. // IndexFeatures must have been called already. void TrainingSampleSet::DeleteOutliers(const IntFeatureSpace& feature_space, bool debug) { if (font_class_array_ == NULL) OrganizeByFontAndClass(); Pixa* pixa = NULL; if (debug) pixa = pixaCreate(0); GenericVector feature_counts; int fs_size = feature_space.Size(); int font_size = font_id_map_.CompactSize(); for (int font_index = 0; font_index < font_size; ++font_index) { for (int c = 0; c < unicharset_size_; ++c) { // Create a histogram of the features used by all samples of this // font/class combination. feature_counts.init_to_size(fs_size, 0); FontClassInfo& fcinfo = (*font_class_array_)(font_index, c); int sample_count = fcinfo.samples.size(); if (sample_count < kMinOutlierSamples) continue; for (int i = 0; i < sample_count; ++i) { int s = fcinfo.samples[i]; const GenericVector& features = samples_[s]->indexed_features(); for (int f = 0; f < features.size(); ++f) { ++feature_counts[features[f]]; } } for (int i = 0; i < sample_count; ++i) { int s = fcinfo.samples[i]; const TrainingSample& sample = *samples_[s]; const GenericVector& features = sample.indexed_features(); // A feature that has a histogram count of 1 is only used by this // sample, making it 'bad'. All others are 'good'. int good_features = 0; int bad_features = 0; for (int f = 0; f < features.size(); ++f) { if (feature_counts[features[f]] > 1) ++good_features; else ++bad_features; } // If more than 1/3 features are bad, then this is an outlier. if (bad_features * 2 > good_features) { tprintf("Deleting outlier sample of %s, %d good, %d bad\n", SampleToString(sample).string(), good_features, bad_features); if (debug) { pixaAddPix(pixa, sample.RenderToPix(&unicharset_), L_INSERT); // Add the previous sample as well, so it is easier to see in // the output what is wrong with this sample. int t; if (i == 0) t = fcinfo.samples[1]; else t = fcinfo.samples[i - 1]; const TrainingSample &csample = *samples_[t]; pixaAddPix(pixa, csample.RenderToPix(&unicharset_), L_INSERT); } // Mark the sample for deletion. KillSample(samples_[s]); } } } } // Truly delete all bad samples and renumber everything. DeleteDeadSamples(); if (pixa != NULL) { Pix* pix = pixaDisplayTiledInRows(pixa, 1, 2600, 1.0, 0, 10, 10); pixaDestroy(&pixa); pixWrite("outliers.png", pix, IFF_PNG); pixDestroy(&pix); } } // Marks the given sample index for deletion. // Deletion is actually completed by DeleteDeadSamples. void TrainingSampleSet::KillSample(TrainingSample* sample) { sample->set_sample_index(-1); } // Deletes all samples with zero features marked by KillSample. void TrainingSampleSet::DeleteDeadSamples() { samples_.compact( NewPermanentTessCallback(this, &TrainingSampleSet::DeleteableSample)); num_raw_samples_ = samples_.size(); // Samples must be re-organized now we have deleted a few. } // Callback function returns true if the given sample is to be deleted, due // to having a negative classid. bool TrainingSampleSet::DeleteableSample(const TrainingSample* sample) { return sample == NULL || sample->class_id() < 0; } static Pix* DebugSample(const UNICHARSET& unicharset, TrainingSample* sample) { tprintf("\nOriginal features:\n"); for (int i = 0; i < sample->num_features(); ++i) { sample->features()[i].print(); } if (sample->features_are_mapped()) { tprintf("\nMapped features:\n"); for (int i = 0; i < sample->mapped_features().size(); ++i) { tprintf("%d ", sample->mapped_features()[i]); } tprintf("\n"); } return sample->RenderToPix(&unicharset); } // Construct an array to access the samples by font,class pair. void TrainingSampleSet::OrganizeByFontAndClass() { // Font indexes are sparse, so we used a map to compact them, so we can // have an efficient 2-d array of fonts and character classes. SetupFontIdMap(); int compact_font_size = font_id_map_.CompactSize(); // Get a 2-d array of generic vectors. if (font_class_array_ != NULL) delete font_class_array_; FontClassInfo empty; font_class_array_ = new GENERIC_2D_ARRAY( compact_font_size, unicharset_size_, empty); for (int s = 0; s < samples_.size(); ++s) { int font_id = samples_[s]->font_id(); int class_id = samples_[s]->class_id(); if (font_id < 0 || font_id >= font_id_map_.SparseSize()) { tprintf("Font id = %d/%d, class id = %d/%d on sample %d\n", font_id, font_id_map_.SparseSize(), class_id, unicharset_size_, s); } ASSERT_HOST(font_id >= 0 && font_id < font_id_map_.SparseSize()); ASSERT_HOST(class_id >= 0 && class_id < unicharset_size_); int font_index = font_id_map_.SparseToCompact(font_id); (*font_class_array_)(font_index, class_id).samples.push_back(s); } // Set the num_raw_samples member of the FontClassInfo, to set the boundary // between the raw samples and the replicated ones. for (int f = 0; f < compact_font_size; ++f) { for (int c = 0; c < unicharset_size_; ++c) (*font_class_array_)(f, c).num_raw_samples = (*font_class_array_)(f, c).samples.size(); } // This is the global number of samples and also marks the boundary between // real and replicated samples. num_raw_samples_ = samples_.size(); } // Constructs the font_id_map_ which maps real font_ids (sparse) to a compact // index for the font_class_array_. void TrainingSampleSet::SetupFontIdMap() { // Number of samples for each font_id. GenericVector font_counts; for (int s = 0; s < samples_.size(); ++s) { int font_id = samples_[s]->font_id(); while (font_id >= font_counts.size()) font_counts.push_back(0); ++font_counts[font_id]; } font_id_map_.Init(font_counts.size(), false); for (int f = 0; f < font_counts.size(); ++f) { font_id_map_.SetMap(f, font_counts[f] > 0); } font_id_map_.Setup(); } // Finds the sample for each font, class pair that has least maximum // distance to all the other samples of the same font, class. // OrganizeByFontAndClass must have been already called. void TrainingSampleSet::ComputeCanonicalSamples(const IntFeatureMap& map, bool debug) { ASSERT_HOST(font_class_array_ != NULL); IntFeatureDist f_table; if (debug) tprintf("feature table size %d\n", map.sparse_size()); f_table.Init(&map); int worst_s1 = 0; int worst_s2 = 0; double global_worst_dist = 0.0; // Compute distances independently for each font and char index. int font_size = font_id_map_.CompactSize(); for (int font_index = 0; font_index < font_size; ++font_index) { int font_id = font_id_map_.CompactToSparse(font_index); for (int c = 0; c < unicharset_size_; ++c) { int samples_found = 0; FontClassInfo& fcinfo = (*font_class_array_)(font_index, c); if (fcinfo.samples.size() == 0 || (kTestChar >= 0 && c != kTestChar)) { fcinfo.canonical_sample = -1; fcinfo.canonical_dist = 0.0f; if (debug) tprintf("Skipping class %d\n", c); continue; } // The canonical sample will be the one with the min_max_dist, which // is the sample with the lowest maximum distance to all other samples. double min_max_dist = 2.0; // We keep track of the farthest apart pair (max_s1, max_s2) which // are max_max_dist apart, so we can see how bad the variability is. double max_max_dist = 0.0; int max_s1 = 0; int max_s2 = 0; fcinfo.canonical_sample = fcinfo.samples[0]; fcinfo.canonical_dist = 0.0f; for (int i = 0; i < fcinfo.samples.size(); ++i) { int s1 = fcinfo.samples[i]; const GenericVector& features1 = samples_[s1]->indexed_features(); f_table.Set(features1, features1.size(), true); double max_dist = 0.0; // Run the full squared-order search for similar samples. It is still // reasonably fast because f_table.FeatureDistance is fast, but we // may have to reconsider if we start playing with too many samples // of a single char/font. for (int j = 0; j < fcinfo.samples.size(); ++j) { int s2 = fcinfo.samples[j]; if (samples_[s2]->class_id() != c || samples_[s2]->font_id() != font_id || s2 == s1) continue; GenericVector features2 = samples_[s2]->indexed_features(); double dist = f_table.FeatureDistance(features2); if (dist > max_dist) { max_dist = dist; if (dist > max_max_dist) { max_s1 = s1; max_s2 = s2; } } } // Using Set(..., false) is far faster than re initializing, due to // the sparseness of the feature space. f_table.Set(features1, features1.size(), false); samples_[s1]->set_max_dist(max_dist); ++samples_found; if (max_dist < min_max_dist) { fcinfo.canonical_sample = s1; fcinfo.canonical_dist = max_dist; } UpdateRange(max_dist, &min_max_dist, &max_max_dist); } if (max_max_dist > global_worst_dist) { // Keep a record of the worst pair over all characters/fonts too. global_worst_dist = max_max_dist; worst_s1 = max_s1; worst_s2 = max_s2; } if (debug) { tprintf("Found %d samples of class %d=%s, font %d, " "dist range [%g, %g], worst pair= %s, %s\n", samples_found, c, unicharset_.debug_str(c).string(), font_index, min_max_dist, max_max_dist, SampleToString(*samples_[max_s1]).string(), SampleToString(*samples_[max_s2]).string()); } } } if (debug) { tprintf("Global worst dist = %g, between sample %d and %d\n", global_worst_dist, worst_s1, worst_s2); Pix* pix1 = DebugSample(unicharset_, samples_[worst_s1]); Pix* pix2 = DebugSample(unicharset_, samples_[worst_s2]); pixOr(pix1, pix1, pix2); pixWrite("worstpair.png", pix1, IFF_PNG); pixDestroy(&pix1); pixDestroy(&pix2); } } // Replicates the samples to a minimum frequency defined by // 2 * kSampleRandomSize, or for larger counts duplicates all samples. // After replication, the replicated samples are perturbed slightly, but // in a predictable and repeatable way. // Use after OrganizeByFontAndClass(). void TrainingSampleSet::ReplicateAndRandomizeSamples() { ASSERT_HOST(font_class_array_ != NULL); int font_size = font_id_map_.CompactSize(); for (int font_index = 0; font_index < font_size; ++font_index) { for (int c = 0; c < unicharset_size_; ++c) { FontClassInfo& fcinfo = (*font_class_array_)(font_index, c); int sample_count = fcinfo.samples.size(); int min_samples = 2 * MAX(kSampleRandomSize, sample_count); if (sample_count > 0 && sample_count < min_samples) { int base_count = sample_count; for (int base_index = 0; sample_count < min_samples; ++sample_count) { int src_index = fcinfo.samples[base_index++]; if (base_index >= base_count) base_index = 0; TrainingSample* sample = samples_[src_index]->RandomizedCopy( sample_count % kSampleRandomSize); int sample_index = samples_.size(); sample->set_sample_index(sample_index); samples_.push_back(sample); fcinfo.samples.push_back(sample_index); } } } } } // Caches the indexed features of the canonical samples. // ComputeCanonicalSamples must have been already called. // TODO(rays) see note on ReliablySeparable and try restricting the // canonical features to those that truly represent all samples. void TrainingSampleSet::ComputeCanonicalFeatures() { ASSERT_HOST(font_class_array_ != NULL); int font_size = font_id_map_.CompactSize(); for (int font_index = 0; font_index < font_size; ++font_index) { int font_id = font_id_map_.CompactToSparse(font_index); for (int c = 0; c < unicharset_size_; ++c) { int num_samples = NumClassSamples(font_id, c, false); if (num_samples == 0) continue; const TrainingSample* sample = GetCanonicalSample(font_id, c); FontClassInfo& fcinfo = (*font_class_array_)(font_index, c); fcinfo.canonical_features = sample->indexed_features(); } } } // Computes the combined set of features used by all the samples of each // font/class combination. Use after ReplicateAndRandomizeSamples. void TrainingSampleSet::ComputeCloudFeatures(int feature_space_size) { ASSERT_HOST(font_class_array_ != NULL); int font_size = font_id_map_.CompactSize(); for (int font_index = 0; font_index < font_size; ++font_index) { int font_id = font_id_map_.CompactToSparse(font_index); for (int c = 0; c < unicharset_size_; ++c) { int num_samples = NumClassSamples(font_id, c, false); if (num_samples == 0) continue; FontClassInfo& fcinfo = (*font_class_array_)(font_index, c); fcinfo.cloud_features.Init(feature_space_size); for (int s = 0; s < num_samples; ++s) { const TrainingSample* sample = GetSample(font_id, c, s); const GenericVector& sample_features = sample->indexed_features(); for (int i = 0; i < sample_features.size(); ++i) fcinfo.cloud_features.SetBit(sample_features[i]); } } } } // Adds all fonts of the given class to the shape. void TrainingSampleSet::AddAllFontsForClass(int class_id, Shape* shape) const { for (int f = 0; f < font_id_map_.CompactSize(); ++f) { int font_id = font_id_map_.CompactToSparse(f); shape->AddToShape(class_id, font_id); } } // Display the samples with the given indexed feature that also match // the given shape. void TrainingSampleSet::DisplaySamplesWithFeature(int f_index, const Shape& shape, const IntFeatureSpace& space, ScrollView::Color color, ScrollView* window) const { for (int s = 0; s < num_raw_samples(); ++s) { const TrainingSample* sample = GetSample(s); if (shape.ContainsUnichar(sample->class_id())) { GenericVector indexed_features; space.IndexAndSortFeatures(sample->features(), sample->num_features(), &indexed_features); for (int f = 0; f < indexed_features.size(); ++f) { if (indexed_features[f] == f_index) { sample->DisplayFeatures(color, window); } } } } } } // namespace tesseract. tesseract-3.04.01/classify/trainingsampleset.h000066400000000000000000000276241266071204500214110ustar00rootroot00000000000000// Copyright 2010 Google Inc. All Rights Reserved. // Author: rays@google.com (Ray Smith) // // 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_TRAININGSAMPLESET_H__ #define TESSERACT_TRAINING_TRAININGSAMPLESET_H__ #include "bitvector.h" #include "genericvector.h" #include "indexmapbidi.h" #include "matrix.h" #include "shapetable.h" #include "trainingsample.h" class UNICHARSET; namespace tesseract { struct FontInfo; class FontInfoTable; class IntFeatureMap; class IntFeatureSpace; class TrainingSample; struct UnicharAndFonts; // Collection of TrainingSample used for training or testing a classifier. // Provides several useful methods to operate on the collection as a whole, // including outlier detection and deletion, providing access by font and // class, finding the canonical sample, finding the "cloud" features (OR of // all features in all samples), replication of samples, caching of distance // metrics. class TrainingSampleSet { public: explicit TrainingSampleSet(const FontInfoTable& fontinfo_table); ~TrainingSampleSet(); // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); // Accessors int num_samples() const { return samples_.size(); } int num_raw_samples() const { return num_raw_samples_; } int NumFonts() const { return font_id_map_.SparseSize(); } const UNICHARSET& unicharset() const { return unicharset_; } int charsetsize() const { return unicharset_size_; } const FontInfoTable& fontinfo_table() const { return fontinfo_table_; } // Loads an initial unicharset, or sets one up if the file cannot be read. void LoadUnicharset(const char* filename); // Adds a character sample to this sample set. // If the unichar is not already in the local unicharset, it is added. // Returns the unichar_id of the added sample, from the local unicharset. int AddSample(const char* unichar, TrainingSample* sample); // Adds a character sample to this sample set with the given unichar_id, // which must correspond to the local unicharset (in this). void AddSample(int unichar_id, TrainingSample* sample); // Returns the number of samples for the given font,class pair. // If randomize is true, returns the number of samples accessible // with randomizing on. (Increases the number of samples if small.) // OrganizeByFontAndClass must have been already called. int NumClassSamples(int font_id, int class_id, bool randomize) const; // Gets a sample by its index. const TrainingSample* GetSample(int index) const; // Gets a sample by its font, class, index. // OrganizeByFontAndClass must have been already called. const TrainingSample* GetSample(int font_id, int class_id, int index) const; // Get a sample by its font, class, index. Does not randomize. // OrganizeByFontAndClass must have been already called. TrainingSample* MutableSample(int font_id, int class_id, int index); // Returns a string debug representation of the given sample: // font, unichar_str, bounding box, page. STRING SampleToString(const TrainingSample& sample) const; // Gets the combined set of features used by all the samples of the given // font/class combination. const BitVector& GetCloudFeatures(int font_id, int class_id) const; // Gets the indexed features of the canonical sample of the given // font/class combination. const GenericVector& GetCanonicalFeatures(int font_id, int class_id) const; // Returns the distance between the given UniCharAndFonts pair. // If matched_fonts, only matching fonts, are considered, unless that yields // the empty set. // OrganizeByFontAndClass must have been already called. float UnicharDistance(const UnicharAndFonts& uf1, const UnicharAndFonts& uf2, bool matched_fonts, const IntFeatureMap& feature_map); // Returns the distance between the given pair of font/class pairs. // Finds in cache or computes and caches. // OrganizeByFontAndClass must have been already called. float ClusterDistance(int font_id1, int class_id1, int font_id2, int class_id2, const IntFeatureMap& feature_map); // Computes the distance between the given pair of font/class pairs. float ComputeClusterDistance(int font_id1, int class_id1, int font_id2, int class_id2, const IntFeatureMap& feature_map) const; // Returns the number of canonical features of font/class 2 for which // neither the feature nor any of its near neighbors occurs in the cloud // of font/class 1. Each such feature is a reliable separation between // the classes, ASSUMING that the canonical sample is sufficiently // representative that every sample has a feature near that particular // feature. To check that this is so on the fly would be prohibitively // expensive, but it might be possible to pre-qualify the canonical features // to include only those for which this assumption is true. // ComputeCanonicalFeatures and ComputeCloudFeatures must have been called // first, or the results will be nonsense. int ReliablySeparable(int font_id1, int class_id1, int font_id2, int class_id2, const IntFeatureMap& feature_map, bool thorough) const; // Returns the total index of the requested sample. // OrganizeByFontAndClass must have been already called. int GlobalSampleIndex(int font_id, int class_id, int index) const; // Gets the canonical sample for the given font, class pair. // ComputeCanonicalSamples must have been called first. const TrainingSample* GetCanonicalSample(int font_id, int class_id) const; // Gets the max distance for the given canonical sample. // ComputeCanonicalSamples must have been called first. float GetCanonicalDist(int font_id, int class_id) const; // Returns a mutable pointer to the sample with the given index. TrainingSample* mutable_sample(int index) { return samples_[index]; } // Gets ownership of the sample with the given index, removing it from this. TrainingSample* extract_sample(int index) { TrainingSample* sample = samples_[index]; samples_[index] = NULL; return sample; } // Generates indexed features for all samples with the supplied feature_space. void IndexFeatures(const IntFeatureSpace& feature_space); // Delete outlier samples with few features that are shared with others. // IndexFeatures must have been called already. void DeleteOutliers(const IntFeatureSpace& feature_space, bool debug); // Marks the given sample for deletion. // Deletion is actually completed by DeleteDeadSamples. void KillSample(TrainingSample* sample); // Deletes all samples with a negative sample index marked by KillSample. // Must be called before OrganizeByFontAndClass, and OrganizeByFontAndClass // must be called after as the samples have been renumbered. void DeleteDeadSamples(); // Callback function returns true if the given sample is to be deleted, due // to having a negative classid. bool DeleteableSample(const TrainingSample* sample); // Construct an array to access the samples by font,class pair. void OrganizeByFontAndClass(); // Constructs the font_id_map_ which maps real font_ids (sparse) to a compact // index for the font_class_array_. void SetupFontIdMap(); // Finds the sample for each font, class pair that has least maximum // distance to all the other samples of the same font, class. // OrganizeByFontAndClass must have been already called. void ComputeCanonicalSamples(const IntFeatureMap& map, bool debug); // Replicates the samples to a minimum frequency defined by // 2 * kSampleRandomSize, or for larger counts duplicates all samples. // After replication, the replicated samples are perturbed slightly, but // in a predictable and repeatable way. // Use after OrganizeByFontAndClass(). void ReplicateAndRandomizeSamples(); // Caches the indexed features of the canonical samples. // ComputeCanonicalSamples must have been already called. void ComputeCanonicalFeatures(); // Computes the combined set of features used by all the samples of each // font/class combination. Use after ReplicateAndRandomizeSamples. void ComputeCloudFeatures(int feature_space_size); // Adds all fonts of the given class to the shape. void AddAllFontsForClass(int class_id, Shape* shape) const; // Display the samples with the given indexed feature that also match // the given shape. void DisplaySamplesWithFeature(int f_index, const Shape& shape, const IntFeatureSpace& feature_space, ScrollView::Color color, ScrollView* window) const; private: // Struct to store a triplet of unichar, font, distance in the distance cache. struct FontClassDistance { int unichar_id; int font_id; // Real font id. float distance; }; // Simple struct to store information related to each font/class combination. struct FontClassInfo { FontClassInfo(); // Writes to the given file. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads from the given file. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); // Number of raw samples. inT32 num_raw_samples; // Index of the canonical sample. inT32 canonical_sample; // Max distance of the canonical sample from any other. float canonical_dist; // Sample indices for the samples, including replicated. GenericVector samples; // Non-serialized cache data. // Indexed features of the canonical sample. GenericVector canonical_features; // The mapped features of all the samples. BitVector cloud_features; // Caches for ClusterDistance. // Caches for other fonts but matching this unichar. -1 indicates not set. // Indexed by compact font index from font_id_map_. GenericVector font_distance_cache; // Caches for other unichars but matching this font. -1 indicates not set. GenericVector unichar_distance_cache; // Cache for the rest (non matching font and unichar.) // A cache of distances computed by ReliablySeparable. GenericVector distance_cache; }; PointerVector samples_; // Number of samples before replication/randomization. int num_raw_samples_; // Character set we are training for. UNICHARSET unicharset_; // Character set size to which the 2-d arrays below refer. int unicharset_size_; // Map to allow the font_class_array_ below to be compact. // The sparse space is the real font_id, used in samples_ . // The compact space is an index to font_class_array_ IndexMapBiDi font_id_map_; // A 2-d array of FontClassInfo holding information related to each // (font_id, class_id) pair. GENERIC_2D_ARRAY* font_class_array_; // Reference to the fontinfo_table_ in MasterTrainer. Provides names // for font_ids in the samples. Not serialized! const FontInfoTable& fontinfo_table_; }; } // namespace tesseract. #endif // TRAININGSAMPLESETSET_H_ tesseract-3.04.01/config/000077500000000000000000000000001266071204500151245ustar00rootroot00000000000000tesseract-3.04.01/config/config.guess000077500000000000000000001311101266071204500174410ustar00rootroot00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2013 Free Software Foundation, Inc. timestamp='2013-06-10' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD # # Please send patches with a ChangeLog entry to config-patches@gnu.org. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2013 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "${UNAME_SYSTEM}" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval $set_cc_for_build cat <<-EOF > $dummy.c #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` ;; esac case "${UNAME_MACHINE}" in i?86) test -z "$VENDOR" && VENDOR=pc ;; *) test -z "$VENDOR" && VENDOR=unknown ;; esac test -f /etc/SuSE-release -o -f /.buildenv && VENDOR=suse # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-${VENDOR}-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-${VENDOR}-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-${VENDOR}-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-${VENDOR}-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-${VENDOR}-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-${VENDOR}-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-${VENDOR}-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-${VENDOR}-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-${VENDOR}-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-${VENDOR}-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-${VENDOR}-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-${VENDOR}-osf1mk else echo ${UNAME_MACHINE}-${VENDOR}-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-${VENDOR}-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-${VENDOR}-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-${VENDOR}-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-${VENDOR}-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW64*:*) echo ${UNAME_MACHINE}-pc-mingw64 exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; i*:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-${VENDOR}-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-${VENDOR}-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-${VENDOR}-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-${VENDOR}-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-${VENDOR}-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-${VENDOR}-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-${VENDOR}-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="gnulibc1" ; fi echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC}eabi else echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-${VENDOR}-linux-${LIBC}"; exit; } ;; or1k:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; or32:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; padre:Linux:*:*) echo sparc-${VENDOR}-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-${VENDOR}-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-${VENDOR}-linux-${LIBC} ;; PA8*) echo hppa2.0-${VENDOR}-linux-${LIBC} ;; *) echo hppa-${VENDOR}-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-${VENDOR}-linux-${LIBC} exit ;; ppc:Linux:*:*) echo powerpc-${VENDOR}-linux-${LIBC} exit ;; ppc64le:Linux:*:*) echo powerpc64le-${VENDOR}-linux-${LIBC} exit ;; ppcle:Linux:*:*) echo powerpcle-${VENDOR}-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-${VENDOR}-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-${VENDOR}-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-${VENODR}-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-${VENDOR}-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-${VENODR}-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-${VENDOR}-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-${VENDOR}-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-${VENDOR}-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-${VENDOR}-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-${VENDOR}-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-${VENDOR}-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval $set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-${VENDOR}-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-${VENDOR}-tops10 exit ;; *:TENEX:*:*) echo pdp10-${VENDOR}-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-${VENDOR}-tops20 exit ;; *:ITS:*:*) echo pdp10-${VENDOR}-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-${VENDOR}-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-${VENDOR}-esx exit ;; esac eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix\n"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; c34*) echo c34-convex-bsd exit ;; c38*) echo c38-convex-bsd exit ;; c4*) echo c4-convex-bsd exit ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: tesseract-3.04.01/config/config.h.in000066400000000000000000000111301266071204500171430ustar00rootroot00000000000000/* config/config.h.in. Generated from configure.ac by autoheader. */ #ifndef CONFIG_AUTO_H #define CONFIG_AUTO_H /* config_auto.h: begin */ /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* Define to be the git revision */ #undef GIT_REV /* Disable graphics */ #undef GRAPHICS_DISABLED /* Define to 1 if you have the header file. */ #undef HAVE_CAIRO_CAIRO_VERSION_H /* Define to 1 if you have the header file. */ #undef HAVE_CL_CL_H /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define if you have the OpenCL framework */ #undef HAVE_FRAMEWORK_OPENCL /* Define to 1 if you have the `getline' function. */ #undef HAVE_GETLINE /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the `lept' library (-llept). */ #undef HAVE_LIBLEPT /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H /* Define to 1 if the system has the type `long long int'. */ #undef HAVE_LONG_LONG_INT /* Define to 1 if you have the header file. */ #undef HAVE_MALLOC_H /* Define to 1 if the system has the type `mbstate_t'. */ #undef HAVE_MBSTATE_T /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if the system has the type `off_t'. */ #undef HAVE_OFF_T /* Define to 1 if you have the header file. */ #undef HAVE_OPENCL_CL_H /* Define to 1 if you have the header file. */ #undef HAVE_PANGO_1_0_PANGO_PANGO_FEATURES_H /* Define to 1 if you have the `snprintf' function. */ #undef HAVE_SNPRINTF /* Define to 1 if stdbool.h conforms to C99. */ #undef HAVE_STDBOOL_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_IPC_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SHM_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have that is POSIX.1 compatible. */ #undef HAVE_SYS_WAIT_H /* Define to 1 if you have the header file. */ #undef HAVE_TIFFIO_H /* Define to 1 if you have the header file. */ #undef HAVE_UNICODE_UCHAR_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if the system has the type `wchar_t'. */ #undef HAVE_WCHAR_T /* Define to 1 if the system has the type `_Bool'. */ #undef HAVE__BOOL /* Define to the sub-directory in which libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* This is a MinGW system */ #undef MINGW /* Defined when compiled with OpenMP support */ #undef OPENMP /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Official date of release */ #undef PACKAGE_DATE /* Name of package */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Version number */ #undef PACKAGE_VERSION /* Official year for this release */ #undef PACKAGE_YEAR /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define to 1 if you can safely include both and . */ #undef TIME_WITH_SYS_TIME /* Version number of package */ #undef VERSION /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif /* Enable large inode numbers on Mac OS X 10.5. */ #ifndef _DARWIN_USE_64_BIT_INODE # define _DARWIN_USE_64_BIT_INODE 1 #endif /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS /* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES /* Miscellaneous defines */ #define AUTOCONF 1 /* Not used yet #ifndef NO_GETTEXT #define USING_GETTEXT #endif */ /* config_auto.h: end */ #endif tesseract-3.04.01/config/config.sub000077500000000000000000001053011266071204500171070ustar00rootroot00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2013 Free Software Foundation, Inc. timestamp='2013-04-24' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches with a ChangeLog entry to config-patches@gnu.org. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2013 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | be32 | be64 \ | bfin \ | c4x | clipper \ | d10v | d30v | dlx | dsp16xx \ | epiphany \ | fido | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 \ | or1k | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i386-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i386-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or1k-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: tesseract-3.04.01/config/depcomp000077500000000000000000000560161266071204500165110ustar00rootroot00000000000000#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2013-05-30.07; # UTC # Copyright (C) 1999-2013 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: tesseract-3.04.01/config/install-sh000077500000000000000000000332551266071204500171400ustar00rootroot00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2011-11-20.07; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: tesseract-3.04.01/config/ltmain.sh000066400000000000000000010515221266071204500167520ustar00rootroot00000000000000 # libtool (GNU libtool) 2.4.2 # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, # 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, # or obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Usage: $progname [OPTION]... [MODE-ARG]... # # Provide generalized library-building support services. # # --config show all configuration variables # --debug enable verbose shell tracing # -n, --dry-run display commands without modifying any files # --features display basic configuration information and exit # --mode=MODE use operation mode MODE # --preserve-dup-deps don't remove duplicate dependency libraries # --quiet, --silent don't print informational messages # --no-quiet, --no-silent # print informational messages (default) # --no-warn don't display warning messages # --tag=TAG use configuration variables from tag TAG # -v, --verbose print more informational messages than default # --no-verbose don't print the extra informational messages # --version print version information # -h, --help, --help-all print short, long, or detailed help message # # MODE must be one of the following: # # clean remove files from the build directory # compile compile a source file into a libtool object # execute automatically set library path, then run a program # finish complete the installation of libtool libraries # install install libraries or executables # link create a library or an executable # uninstall remove libraries from an installed directory # # MODE-ARGS vary depending on the MODE. When passed as first option, # `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that. # Try `$progname --help --mode=MODE' for a more detailed description of MODE. # # When reporting a bug, please describe a test case to reproduce it and # include the following information: # # host-triplet: $host # shell: $SHELL # compiler: $LTCC # compiler flags: $LTCFLAGS # linker: $LD (gnu? $with_gnu_ld) # $progname: (GNU libtool) 2.4.2 # automake: $automake_version # autoconf: $autoconf_version # # Report bugs to . # GNU libtool home page: . # General help using GNU software: . PROGRAM=libtool PACKAGE=libtool VERSION=2.4.2 TIMESTAMP="" package_revision=1.3337 # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } # NLS nuisances: We save the old values to restore during execute mode. lt_user_locale= lt_safe_locale= for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${$lt_var+set}\" = set; then save_$lt_var=\$$lt_var $lt_var=C export $lt_var lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" fi" done LC_ALL=C LANGUAGE=C export LANGUAGE LC_ALL $lt_unset CDPATH # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath="$0" : ${CP="cp -f"} test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} : ${Xsed="$SED -e 1s/^X//"} # Global variables: EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. exit_status=$EXIT_SUCCESS # Make sure IFS has a sensible default lt_nl=' ' IFS=" $lt_nl" dirname="s,/[^/]*$,," basename="s,^.*/,," # func_dirname file append nondir_replacement # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. func_dirname () { func_dirname_result=`$ECHO "${1}" | $SED "$dirname"` if test "X$func_dirname_result" = "X${1}"; then func_dirname_result="${3}" else func_dirname_result="$func_dirname_result${2}" fi } # func_dirname may be replaced by extended shell implementation # func_basename file func_basename () { func_basename_result=`$ECHO "${1}" | $SED "$basename"` } # func_basename may be replaced by extended shell implementation # func_dirname_and_basename file append nondir_replacement # perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # Implementation must be kept synchronized with func_dirname # and func_basename. For efficiency, we do not delegate to # those functions but instead duplicate the functionality here. func_dirname_and_basename () { # Extract subdirectory from the argument. func_dirname_result=`$ECHO "${1}" | $SED -e "$dirname"` if test "X$func_dirname_result" = "X${1}"; then func_dirname_result="${3}" else func_dirname_result="$func_dirname_result${2}" fi func_basename_result=`$ECHO "${1}" | $SED -e "$basename"` } # func_dirname_and_basename may be replaced by extended shell implementation # func_stripname prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # func_strip_suffix prefix name func_stripname () { case ${2} in .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; esac } # func_stripname may be replaced by extended shell implementation # These SED scripts presuppose an absolute path with a trailing slash. pathcar='s,^/\([^/]*\).*$,\1,' pathcdr='s,^/[^/]*,,' removedotparts=':dotsl s@/\./@/@g t dotsl s,/\.$,/,' collapseslashes='s@/\{1,\}@/@g' finalslash='s,/*$,/,' # func_normal_abspath PATH # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. # value returned in "$func_normal_abspath_result" func_normal_abspath () { # Start from root dir and reassemble the path. func_normal_abspath_result= func_normal_abspath_tpath=$1 func_normal_abspath_altnamespace= case $func_normal_abspath_tpath in "") # Empty path, that just means $cwd. func_stripname '' '/' "`pwd`" func_normal_abspath_result=$func_stripname_result return ;; # The next three entries are used to spot a run of precisely # two leading slashes without using negated character classes; # we take advantage of case's first-match behaviour. ///*) # Unusual form of absolute path, do nothing. ;; //*) # Not necessarily an ordinary path; POSIX reserves leading '//' # and for example Cygwin uses it to access remote file shares # over CIFS/SMB, so we conserve a leading double slash if found. func_normal_abspath_altnamespace=/ ;; /*) # Absolute path, do nothing. ;; *) # Relative path, prepend $cwd. func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac # Cancel out all the simple stuff to save iterations. We also want # the path to end with a slash for ease of parsing, so make sure # there is one (and only one) here. func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"` while :; do # Processed it all yet? if test "$func_normal_abspath_tpath" = / ; then # If we ascended to the root using ".." the result may be empty now. if test -z "$func_normal_abspath_result" ; then func_normal_abspath_result=/ fi break fi func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$pathcar"` func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$pathcdr"` # Figure out what to do with it case $func_normal_abspath_tcomponent in "") # Trailing empty path component, ignore it. ;; ..) # Parent dir; strip last assembled component from result. func_dirname "$func_normal_abspath_result" func_normal_abspath_result=$func_dirname_result ;; *) # Actual path component, append it. func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent ;; esac done # Restore leading double-slash if one was found on entry. func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } # func_relative_path SRCDIR DSTDIR # generates a relative path from SRCDIR to DSTDIR, with a trailing # slash if non-empty, suitable for immediately appending a filename # without needing to append a separator. # value returned in "$func_relative_path_result" func_relative_path () { func_relative_path_result= func_normal_abspath "$1" func_relative_path_tlibdir=$func_normal_abspath_result func_normal_abspath "$2" func_relative_path_tbindir=$func_normal_abspath_result # Ascend the tree starting from libdir while :; do # check if we have found a prefix of bindir case $func_relative_path_tbindir in $func_relative_path_tlibdir) # found an exact match func_relative_path_tcancelled= break ;; $func_relative_path_tlibdir*) # found a matching prefix func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" func_relative_path_tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $func_relative_path_tlibdir func_relative_path_tlibdir=${func_dirname_result} if test "x$func_relative_path_tlibdir" = x ; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result func_relative_path_tcancelled=$func_relative_path_tbindir break fi func_relative_path_result=../$func_relative_path_result ;; esac done # Now calculate path; take care to avoid doubling-up slashes. func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '/' '/' "$func_relative_path_tcancelled" if test "x$func_stripname_result" != x ; then func_relative_path_result=${func_relative_path_result}/${func_stripname_result} fi # Normalisation. If bindir is libdir, return empty string, # else relative path ending with a slash; either way, target # file name can be directly appended. if test ! -z "$func_relative_path_result"; then func_stripname './' '' "$func_relative_path_result/" func_relative_path_result=$func_stripname_result fi } # The name of this program: func_dirname_and_basename "$progpath" progname=$func_basename_result # Make sure we have an absolute path for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=$func_dirname_result progdir=`cd "$progdir" && pwd` progpath="$progdir/$progname" ;; *) save_IFS="$IFS" IFS=${PATH_SEPARATOR-:} for progdir in $PATH; do IFS="$save_IFS" test -x "$progdir/$progname" && break done IFS="$save_IFS" test -n "$progdir" || progdir=`pwd` progpath="$progdir/$progname" ;; esac # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. Xsed="${SED}"' -e 1s/^X//' sed_quote_subst='s/\([`"$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution that turns a string into a regex matching for the # string literally. sed_make_literal_regex='s,[].[^$\\*\/],\\&,g' # Sed substitution that converts a w32 file name or path # which contains forward slashes, into one that contains # (escaped) backslashes. A very naive implementation. lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Re-`\' parameter expansions in output of double_quote_subst that were # `\'-ed in input to the same. If an odd number of `\' preceded a '$' # in input to double_quote_subst, that '$' was protected from expansion. # Since each input `\' is now two `\'s, look for any number of runs of # four `\'s followed by two `\'s and then a '$'. `\' that '$'. bs='\\' bs2='\\\\' bs4='\\\\\\\\' dollar='\$' sed_double_backslash="\ s/$bs4/&\\ /g s/^$bs2$dollar/$bs&/ s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g s/\n//g" # Standard options: opt_dry_run=false opt_help=false opt_quiet=false opt_verbose=false opt_warning=: # func_echo arg... # Echo program name prefixed message, along with the current mode # name if it has been set yet. func_echo () { $ECHO "$progname: ${opt_mode+$opt_mode: }$*" } # func_verbose arg... # Echo program name prefixed message in verbose mode only. func_verbose () { $opt_verbose && func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } # func_error arg... # Echo program name prefixed message to standard error. func_error () { $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2 } # func_warning arg... # Echo program name prefixed warning message to standard error. func_warning () { $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2 # bash bug again: : } # func_fatal_error arg... # Echo program name prefixed message to standard error, and exit. func_fatal_error () { func_error ${1+"$@"} exit $EXIT_FAILURE } # func_fatal_help arg... # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { func_error ${1+"$@"} func_fatal_error "$help" } help="Try \`$progname --help' for more information." ## default # func_grep expression filename # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $GREP "$1" "$2" >/dev/null 2>&1 } # func_mkdir_p directory-path # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { my_directory_path="$1" my_dir_list= if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then # Protect directory names starting with `-' case $my_directory_path in -*) my_directory_path="./$my_directory_path" ;; esac # While some portion of DIR does not yet exist... while test ! -d "$my_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list incase some portion of path contains whitespace. my_dir_list="$my_directory_path:$my_dir_list" # If the last portion added has no slash in it, the list is done case $my_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"` done my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'` save_mkdir_p_IFS="$IFS"; IFS=':' for my_dir in $my_dir_list; do IFS="$save_mkdir_p_IFS" # mkdir can fail with a `File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$my_dir" 2>/dev/null || : done IFS="$save_mkdir_p_IFS" # Bail out if we (or some other process) failed to create a directory. test -d "$my_directory_path" || \ func_fatal_error "Failed to create \`$1'" fi } # func_mktempdir [string] # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, STRING is the basename for that directory. func_mktempdir () { my_template="${TMPDIR-/tmp}/${1-$progname}" if test "$opt_dry_run" = ":"; then # Return a directory name, but don't create it in dry-run mode my_tmpdir="${my_template}-$$" else # If mktemp works, use that first and foremost my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` if test ! -d "$my_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race my_tmpdir="${my_template}-${RANDOM-0}$$" save_mktempdir_umask=`umask` umask 0077 $MKDIR "$my_tmpdir" umask $save_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$my_tmpdir" || \ func_fatal_error "cannot create temporary directory \`$my_tmpdir'" fi $ECHO "$my_tmpdir" } # func_quote_for_eval arg # Aesthetically quote ARG to be evaled later. # This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT # is double-quoted, suitable for a subsequent eval, whereas # FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters # which are still active within double quotes backslashified. func_quote_for_eval () { case $1 in *[\\\`\"\$]*) func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;; *) func_quote_for_eval_unquoted_result="$1" ;; esac case $func_quote_for_eval_unquoted_result in # Double-quote args containing shell metacharacters to delay # word splitting, command substitution and and variable # expansion for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" ;; *) func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" esac } # func_quote_for_expand arg # Aesthetically quote ARG to be evaled later; same as above, # but do not quote variable references. func_quote_for_expand () { case $1 in *[\\\`\"]*) my_arg=`$ECHO "$1" | $SED \ -e "$double_quote_subst" -e "$sed_double_backslash"` ;; *) my_arg="$1" ;; esac case $my_arg in # Double-quote args containing shell metacharacters to delay # word splitting and command substitution for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") my_arg="\"$my_arg\"" ;; esac func_quote_for_expand_result="$my_arg" } # func_show_eval cmd [fail_exp] # Unless opt_silent is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { my_cmd="$1" my_fail_exp="${2-:}" ${opt_silent-false} || { func_quote_for_expand "$my_cmd" eval "func_echo $func_quote_for_expand_result" } if ${opt_dry_run-false}; then :; else eval "$my_cmd" my_status=$? if test "$my_status" -eq 0; then :; else eval "(exit $my_status); $my_fail_exp" fi fi } # func_show_eval_locale cmd [fail_exp] # Unless opt_silent is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { my_cmd="$1" my_fail_exp="${2-:}" ${opt_silent-false} || { func_quote_for_expand "$my_cmd" eval "func_echo $func_quote_for_expand_result" } if ${opt_dry_run-false}; then :; else eval "$lt_user_locale $my_cmd" my_status=$? eval "$lt_safe_locale" if test "$my_status" -eq 0; then :; else eval "(exit $my_status); $my_fail_exp" fi fi } # func_tr_sh # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { case $1 in [0-9]* | *[!a-zA-Z0-9_]*) func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'` ;; * ) func_tr_sh_result=$1 ;; esac } # func_version # Echo version message to standard output and exit. func_version () { $opt_debug $SED -n '/(C)/!b go :more /\./!{ N s/\n# / / b more } :go /^# '$PROGRAM' (GNU /,/# warranty; / { s/^# // s/^# *$// s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ p }' < "$progpath" exit $? } # func_usage # Echo short help message to standard output and exit. func_usage () { $opt_debug $SED -n '/^# Usage:/,/^# *.*--help/ { s/^# // s/^# *$// s/\$progname/'$progname'/ p }' < "$progpath" echo $ECHO "run \`$progname --help | more' for full usage" exit $? } # func_help [NOEXIT] # Echo long help message to standard output and exit, # unless 'noexit' is passed as argument. func_help () { $opt_debug $SED -n '/^# Usage:/,/# Report bugs to/ { :print s/^# // s/^# *$// s*\$progname*'$progname'* s*\$host*'"$host"'* s*\$SHELL*'"$SHELL"'* s*\$LTCC*'"$LTCC"'* s*\$LTCFLAGS*'"$LTCFLAGS"'* s*\$LD*'"$LD"'* s/\$with_gnu_ld/'"$with_gnu_ld"'/ s/\$automake_version/'"`(${AUTOMAKE-automake} --version) 2>/dev/null |$SED 1q`"'/ s/\$autoconf_version/'"`(${AUTOCONF-autoconf} --version) 2>/dev/null |$SED 1q`"'/ p d } /^# .* home page:/b print /^# General help using/b print ' < "$progpath" ret=$? if test -z "$1"; then exit $ret fi } # func_missing_arg argname # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { $opt_debug func_error "missing argument for $1." exit_cmd=exit } # func_split_short_opt shortopt # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. func_split_short_opt () { my_sed_short_opt='1s/^\(..\).*$/\1/;q' my_sed_short_rest='1s/^..\(.*\)$/\1/;q' func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"` func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"` } # func_split_short_opt may be replaced by extended shell implementation # func_split_long_opt longopt # Set func_split_long_opt_name and func_split_long_opt_arg shell # variables after splitting LONGOPT at the `=' sign. func_split_long_opt () { my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q' my_sed_long_arg='1s/^--[^=]*=//' func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"` func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"` } # func_split_long_opt may be replaced by extended shell implementation exit_cmd=: magic="%%%MAGIC variable%%%" magic_exe="%%%MAGIC EXE variable%%%" # Global variables. nonopt= preserve_args= lo2o="s/\\.lo\$/.${objext}/" o2lo="s/\\.${objext}\$/.lo/" extracted_archives= extracted_serial=0 # If this variable is set in any of the actions, the command in it # will be execed at the end. This prevents here-documents from being # left over by shells. exec_cmd= # func_append var value # Append VALUE to the end of shell variable VAR. func_append () { eval "${1}=\$${1}\${2}" } # func_append may be replaced by extended shell implementation # func_append_quoted var value # Quote VALUE and append to the end of shell variable VAR, separated # by a space. func_append_quoted () { func_quote_for_eval "${2}" eval "${1}=\$${1}\\ \$func_quote_for_eval_result" } # func_append_quoted may be replaced by extended shell implementation # func_arith arithmetic-term... func_arith () { func_arith_result=`expr "${@}"` } # func_arith may be replaced by extended shell implementation # func_len string # STRING may not start with a hyphen. func_len () { func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len` } # func_len may be replaced by extended shell implementation # func_lo2o object func_lo2o () { func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"` } # func_lo2o may be replaced by extended shell implementation # func_xform libobj-or-source func_xform () { func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'` } # func_xform may be replaced by extended shell implementation # func_fatal_configuration arg... # Echo program name prefixed message to standard error, followed by # a configuration failure hint, and exit. func_fatal_configuration () { func_error ${1+"$@"} func_error "See the $PACKAGE documentation for more information." func_fatal_error "Fatal configuration error." } # func_config # Display the configuration for all the tags in this script. func_config () { re_begincf='^# ### BEGIN LIBTOOL' re_endcf='^# ### END LIBTOOL' # Default configuration. $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" # Now print the configurations for the tags. for tagname in $taglist; do $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" done exit $? } # func_features # Display the features supported by this script. func_features () { echo "host: $host" if test "$build_libtool_libs" = yes; then echo "enable shared libraries" else echo "disable shared libraries" fi if test "$build_old_libs" = yes; then echo "enable static libraries" else echo "disable static libraries" fi exit $? } # func_enable_tag tagname # Verify that TAGNAME is valid, and either flag an error and exit, or # enable the TAGNAME tag. We also add TAGNAME to the global $taglist # variable here. func_enable_tag () { # Global variable: tagname="$1" re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" sed_extractcf="/$re_begincf/,/$re_endcf/p" # Validate tagname. case $tagname in *[!-_A-Za-z0-9,/]*) func_fatal_error "invalid tag name: $tagname" ;; esac # Don't test for the "default" C tag, as we know it's # there but not specially marked. case $tagname in CC) ;; *) if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # func_check_version_match # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; esac # Option defaults: opt_debug=: opt_dry_run=false opt_config=false opt_preserve_dup_deps=false opt_features=false opt_finish=false opt_help=false opt_help_all=false opt_silent=: opt_warning=: opt_verbose=: opt_silent=false opt_verbose=false # Parse options once, thoroughly. This comes as soon as possible in the # script to make things like `--version' happen as quickly as we can. { # this just eases exit handling while test $# -gt 0; do opt="$1" shift case $opt in --debug|-x) opt_debug='set -x' func_echo "enabling shell trace mode" $opt_debug ;; --dry-run|--dryrun|-n) opt_dry_run=: ;; --config) opt_config=: func_config ;; --dlopen|-dlopen) optarg="$1" opt_dlopen="${opt_dlopen+$opt_dlopen }$optarg" shift ;; --preserve-dup-deps) opt_preserve_dup_deps=: ;; --features) opt_features=: func_features ;; --finish) opt_finish=: set dummy --mode finish ${1+"$@"}; shift ;; --help) opt_help=: ;; --help-all) opt_help_all=: opt_help=': help-all' ;; --mode) test $# = 0 && func_missing_arg $opt && break optarg="$1" opt_mode="$optarg" case $optarg in # Valid mode arguments: clean|compile|execute|finish|install|link|relink|uninstall) ;; # Catch anything else as an error *) func_error "invalid argument for $opt" exit_cmd=exit break ;; esac shift ;; --no-silent|--no-quiet) opt_silent=false func_append preserve_args " $opt" ;; --no-warning|--no-warn) opt_warning=false func_append preserve_args " $opt" ;; --no-verbose) opt_verbose=false func_append preserve_args " $opt" ;; --silent|--quiet) opt_silent=: func_append preserve_args " $opt" opt_verbose=false ;; --verbose|-v) opt_verbose=: func_append preserve_args " $opt" opt_silent=false ;; --tag) test $# = 0 && func_missing_arg $opt && break optarg="$1" opt_tag="$optarg" func_append preserve_args " $opt $optarg" func_enable_tag "$optarg" shift ;; -\?|-h) func_usage ;; --help) func_help ;; --version) func_version ;; # Separate optargs to long options: --*=*) func_split_long_opt "$opt" set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"} shift ;; # Separate non-argument short options: -\?*|-h*|-n*|-v*) func_split_short_opt "$opt" set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"} shift ;; --) break ;; -*) func_fatal_help "unrecognized option \`$opt'" ;; *) set dummy "$opt" ${1+"$@"}; shift; break ;; esac done # Validate options: # save first non-option argument if test "$#" -gt 0; then nonopt="$opt" shift fi # preserve --debug test "$opt_debug" = : || func_append preserve_args " --debug" case $host in *cygwin* | *mingw* | *pw32* | *cegcc*) # don't eliminate duplications in $postdeps and $predeps opt_duplicate_compiler_generated_deps=: ;; *) opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps ;; esac $opt_help || { # Sanity checks first: func_check_version_match if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then func_fatal_configuration "not configured to build any kind of library" fi # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$opt_dlopen" && test "$opt_mode" != execute; then func_error "unrecognized option \`-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help="$help" help="Try \`$progname --help --mode=$opt_mode' for more information." } # Bail if the options were screwed $exit_cmd $EXIT_FAILURE } ## ----------- ## ## Main. ## ## ----------- ## # func_lalib_p file # True iff FILE is a libtool `.la' library or `.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null \ | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_unsafe_p file # True iff FILE is a libtool `.la' library or `.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if `file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case "$lalib_p_line" in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test "$lalib_p" = yes } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { func_lalib_p "$1" } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $opt_debug save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$save_ifs eval cmd=\"$cmd\" func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # `FILE.' does not work on cygwin managed mounts. func_source () { $opt_debug case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_resolve_sysroot PATH # Replace a leading = in PATH with a sysroot. Store the result into # func_resolve_sysroot_result func_resolve_sysroot () { func_resolve_sysroot_result=$1 case $func_resolve_sysroot_result in =*) func_stripname '=' '' "$func_resolve_sysroot_result" func_resolve_sysroot_result=$lt_sysroot$func_stripname_result ;; esac } # func_replace_sysroot PATH # If PATH begins with the sysroot, replace it with = and # store the result into func_replace_sysroot_result. func_replace_sysroot () { case "$lt_sysroot:$1" in ?*:"$lt_sysroot"*) func_stripname "$lt_sysroot" '' "$1" func_replace_sysroot_result="=$func_stripname_result" ;; *) # Including no sysroot. func_replace_sysroot_result=$1 ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $opt_debug if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case "$@ " in " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with \`--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=${1} if test "$build_libtool_libs" = yes; then write_lobj=\'${2}\' else write_lobj=none fi if test "$build_old_libs" = yes; then write_oldobj=\'${3}\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T </dev/null` if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | $SED -e "$lt_sed_naive_backslashify"` else func_convert_core_file_wine_to_w32_result= fi fi } # end: func_convert_core_file_wine_to_w32 # func_convert_core_path_wine_to_w32 ARG # Helper function used by path conversion functions when $build is *nix, and # $host is mingw, cygwin, or some other w32 environment. Relies on a correctly # configured wine environment available, with the winepath program in $build's # $PATH. Assumes ARG has no leading or trailing path separator characters. # # ARG is path to be converted from $build format to win32. # Result is available in $func_convert_core_path_wine_to_w32_result. # Unconvertible file (directory) names in ARG are skipped; if no directory names # are convertible, then the result may be empty. func_convert_core_path_wine_to_w32 () { $opt_debug # unfortunately, winepath doesn't convert paths, only file names func_convert_core_path_wine_to_w32_result="" if test -n "$1"; then oldIFS=$IFS IFS=: for func_convert_core_path_wine_to_w32_f in $1; do IFS=$oldIFS func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" if test -n "$func_convert_core_file_wine_to_w32_result" ; then if test -z "$func_convert_core_path_wine_to_w32_result"; then func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result" else func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" fi fi done IFS=$oldIFS fi } # end: func_convert_core_path_wine_to_w32 # func_cygpath ARGS... # Wrapper around calling the cygpath program via LT_CYGPATH. This is used when # when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) # $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or # (2), returns the Cygwin file name or path in func_cygpath_result (input # file name or path is assumed to be in w32 format, as previously converted # from $build's *nix or MSYS format). In case (3), returns the w32 file name # or path in func_cygpath_result (input file name or path is assumed to be in # Cygwin format). Returns an empty string on error. # # ARGS are passed to cygpath, with the last one being the file name or path to # be converted. # # Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH # environment variable; do not put it in $PATH. func_cygpath () { $opt_debug if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` if test "$?" -ne 0; then # on failure, ensure result is empty func_cygpath_result= fi else func_cygpath_result= func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'" fi } #end: func_cygpath # func_convert_core_msys_to_w32 ARG # Convert file name or path ARG from MSYS format to w32 format. Return # result in func_convert_core_msys_to_w32_result. func_convert_core_msys_to_w32 () { $opt_debug # awkward: cmd appends spaces to result func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"` } #end: func_convert_core_msys_to_w32 # func_convert_file_check ARG1 ARG2 # Verify that ARG1 (a file name in $build format) was converted to $host # format in ARG2. Otherwise, emit an error message, but continue (resetting # func_to_host_file_result to ARG1). func_convert_file_check () { $opt_debug if test -z "$2" && test -n "$1" ; then func_error "Could not determine host file name corresponding to" func_error " \`$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_file_result="$1" fi } # end func_convert_file_check # func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH # Verify that FROM_PATH (a path in $build format) was converted to $host # format in TO_PATH. Otherwise, emit an error message, but continue, resetting # func_to_host_file_result to a simplistic fallback value (see below). func_convert_path_check () { $opt_debug if test -z "$4" && test -n "$3"; then func_error "Could not determine the host path corresponding to" func_error " \`$3'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This is a deliberately simplistic "conversion" and # should not be "improved". See libtool.info. if test "x$1" != "x$2"; then lt_replace_pathsep_chars="s|$1|$2|g" func_to_host_path_result=`echo "$3" | $SED -e "$lt_replace_pathsep_chars"` else func_to_host_path_result="$3" fi fi } # end func_convert_path_check # func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG # Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT # and appending REPL if ORIG matches BACKPAT. func_convert_path_front_back_pathsep () { $opt_debug case $4 in $1 ) func_to_host_path_result="$3$func_to_host_path_result" ;; esac case $4 in $2 ) func_append func_to_host_path_result "$3" ;; esac } # end func_convert_path_front_back_pathsep ################################################## # $build to $host FILE NAME CONVERSION FUNCTIONS # ################################################## # invoked via `$to_host_file_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # Result will be available in $func_to_host_file_result. # func_to_host_file ARG # Converts the file name ARG from $build format to $host format. Return result # in func_to_host_file_result. func_to_host_file () { $opt_debug $to_host_file_cmd "$1" } # end func_to_host_file # func_to_tool_file ARG LAZY # converts the file name ARG from $build format to toolchain format. Return # result in func_to_tool_file_result. If the conversion in use is listed # in (the comma separated) LAZY, no conversion takes place. func_to_tool_file () { $opt_debug case ,$2, in *,"$to_tool_file_cmd",*) func_to_tool_file_result=$1 ;; *) $to_tool_file_cmd "$1" func_to_tool_file_result=$func_to_host_file_result ;; esac } # end func_to_tool_file # func_convert_file_noop ARG # Copy ARG to func_to_host_file_result. func_convert_file_noop () { func_to_host_file_result="$1" } # end func_convert_file_noop # func_convert_file_msys_to_w32 ARG # Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_file_result. func_convert_file_msys_to_w32 () { $opt_debug func_to_host_file_result="$1" if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_to_host_file_result="$func_convert_core_msys_to_w32_result" fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_w32 # func_convert_file_cygwin_to_w32 ARG # Convert file name ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_file_cygwin_to_w32 () { $opt_debug func_to_host_file_result="$1" if test -n "$1"; then # because $build is cygwin, we call "the" cygpath in $PATH; no need to use # LT_CYGPATH in this case. func_to_host_file_result=`cygpath -m "$1"` fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_cygwin_to_w32 # func_convert_file_nix_to_w32 ARG # Convert file name ARG from *nix to w32 format. Requires a wine environment # and a working winepath. Returns result in func_to_host_file_result. func_convert_file_nix_to_w32 () { $opt_debug func_to_host_file_result="$1" if test -n "$1"; then func_convert_core_file_wine_to_w32 "$1" func_to_host_file_result="$func_convert_core_file_wine_to_w32_result" fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_w32 # func_convert_file_msys_to_cygwin ARG # Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_file_msys_to_cygwin () { $opt_debug func_to_host_file_result="$1" if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_cygpath -u "$func_convert_core_msys_to_w32_result" func_to_host_file_result="$func_cygpath_result" fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_cygwin # func_convert_file_nix_to_cygwin ARG # Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed # in a wine environment, working winepath, and LT_CYGPATH set. Returns result # in func_to_host_file_result. func_convert_file_nix_to_cygwin () { $opt_debug func_to_host_file_result="$1" if test -n "$1"; then # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. func_convert_core_file_wine_to_w32 "$1" func_cygpath -u "$func_convert_core_file_wine_to_w32_result" func_to_host_file_result="$func_cygpath_result" fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_cygwin ############################################# # $build to $host PATH CONVERSION FUNCTIONS # ############################################# # invoked via `$to_host_path_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # The result will be available in $func_to_host_path_result. # # Path separators are also converted from $build format to $host format. If # ARG begins or ends with a path separator character, it is preserved (but # converted to $host format) on output. # # All path conversion functions are named using the following convention: # file name conversion function : func_convert_file_X_to_Y () # path conversion function : func_convert_path_X_to_Y () # where, for any given $build/$host combination the 'X_to_Y' value is the # same. If conversion functions are added for new $build/$host combinations, # the two new functions must follow this pattern, or func_init_to_host_path_cmd # will break. # func_init_to_host_path_cmd # Ensures that function "pointer" variable $to_host_path_cmd is set to the # appropriate value, based on the value of $to_host_file_cmd. to_host_path_cmd= func_init_to_host_path_cmd () { $opt_debug if test -z "$to_host_path_cmd"; then func_stripname 'func_convert_file_' '' "$to_host_file_cmd" to_host_path_cmd="func_convert_path_${func_stripname_result}" fi } # func_to_host_path ARG # Converts the path ARG from $build format to $host format. Return result # in func_to_host_path_result. func_to_host_path () { $opt_debug func_init_to_host_path_cmd $to_host_path_cmd "$1" } # end func_to_host_path # func_convert_path_noop ARG # Copy ARG to func_to_host_path_result. func_convert_path_noop () { func_to_host_path_result="$1" } # end func_convert_path_noop # func_convert_path_msys_to_w32 ARG # Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_path_result. func_convert_path_msys_to_w32 () { $opt_debug func_to_host_path_result="$1" if test -n "$1"; then # Remove leading and trailing path separator characters from ARG. MSYS # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; # and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result="$func_convert_core_msys_to_w32_result" func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_msys_to_w32 # func_convert_path_cygwin_to_w32 ARG # Convert path ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_path_cygwin_to_w32 () { $opt_debug func_to_host_path_result="$1" if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_cygwin_to_w32 # func_convert_path_nix_to_w32 ARG # Convert path ARG from *nix to w32 format. Requires a wine environment and # a working winepath. Returns result in func_to_host_file_result. func_convert_path_nix_to_w32 () { $opt_debug func_to_host_path_result="$1" if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result="$func_convert_core_path_wine_to_w32_result" func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_nix_to_w32 # func_convert_path_msys_to_cygwin ARG # Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_path_msys_to_cygwin () { $opt_debug func_to_host_path_result="$1" if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_msys_to_w32_result" func_to_host_path_result="$func_cygpath_result" func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_msys_to_cygwin # func_convert_path_nix_to_cygwin ARG # Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a # a wine environment, working winepath, and LT_CYGPATH set. Returns result in # func_to_host_file_result. func_convert_path_nix_to_cygwin () { $opt_debug func_to_host_path_result="$1" if test -n "$1"; then # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" func_to_host_path_result="$func_cygpath_result" func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_nix_to_cygwin # func_mode_compile arg... func_mode_compile () { $opt_debug # Get the compilation command and the source file. base_compile= srcfile="$nonopt" # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal libobj= later= pie_flag= for arg do case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile lastarg="$arg" arg_mode=normal ;; target ) libobj="$arg" arg_mode=normal continue ;; normal ) # Accept any command-line options. case $arg in -o) test -n "$libobj" && \ func_fatal_error "you cannot specify \`-o' more than once" arg_mode=target continue ;; -pie | -fpie | -fPIE) func_append pie_flag " $arg" continue ;; -shared | -static | -prefer-pic | -prefer-non-pic) func_append later " $arg" continue ;; -no-suppress) suppress_opt=no continue ;; -Xcompiler) arg_mode=arg # the next one goes into the "base_compile" arg list continue # The current "srcfile" will either be retained or ;; # replaced later. I would guess that would be a bug. -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result lastarg= save_ifs="$IFS"; IFS=',' for arg in $args; do IFS="$save_ifs" func_append_quoted lastarg "$arg" done IFS="$save_ifs" func_stripname ' ' '' "$lastarg" lastarg=$func_stripname_result # Add the arguments to base_compile. func_append base_compile " $lastarg" continue ;; *) # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # lastarg="$srcfile" srcfile="$arg" ;; esac # case $arg ;; esac # case $arg_mode # Aesthetically quote the previous argument. func_append_quoted base_compile "$lastarg" done # for arg case $arg_mode in arg) func_fatal_error "you must specify an argument for -Xcompile" ;; target) func_fatal_error "you must specify a target with \`-o'" ;; *) # Get the name of the library object. test -z "$libobj" && { func_basename "$srcfile" libobj="$func_basename_result" } ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo case $libobj in *.[cCFSifmso] | \ *.ada | *.adb | *.ads | *.asm | \ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) func_xform "$libobj" libobj=$func_xform_result ;; esac case $libobj in *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) func_fatal_error "cannot determine name of library object from \`$libobj'" ;; esac func_infer_tag $base_compile for arg in $later; do case $arg in -shared) test "$build_libtool_libs" != yes && \ func_fatal_configuration "can not build a shared library" build_old_libs=no continue ;; -static) build_libtool_libs=no build_old_libs=yes continue ;; -prefer-pic) pic_mode=yes continue ;; -prefer-non-pic) pic_mode=no continue ;; esac done func_quote_for_eval "$libobj" test "X$libobj" != "X$func_quote_for_eval_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name \`$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname="$func_basename_result" xdir="$func_dirname_result" lobj=${xdir}$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test "$build_old_libs" = yes; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test "$compiler_c_o" = no; then output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext} lockfile="$output_obj.lock" else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test "$need_locks" = yes; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test "$need_locks" = warn; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi func_append removelist " $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist func_append removelist " $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result func_quote_for_eval "$srcfile" qsrcfile=$func_quote_for_eval_result # Only build a PIC object if we are building libtool libraries. if test "$build_libtool_libs" = yes; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test "$pic_mode" != no; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir func_append command " -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test "$need_locks" = warn && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test "$suppress_opt" = yes; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test "$build_old_libs" = yes; then if test "$pic_mode" != yes; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test "$compiler_c_o" = yes; then func_append command " -o $obj" fi # Suppress compiler output if we already did a PIC compilation. func_append command "$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test "$need_locks" = warn && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test "$need_locks" != no; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test "$opt_mode" = compile && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $opt_mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to build PIC objects only -prefer-non-pic try to build non-PIC objects only -shared do not build a \`.o' file suitable for static linking -static only build a \`.o' file suitable for static linking -Wc,FLAG pass FLAG directly to the compiler COMPILE-COMMAND is a command to be used in creating a \`standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix \`.c' with the library object suffix, \`.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to \`-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the \`--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the \`install' or \`cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -bindir BINDIR specify path to binaries directory (for systems where libraries must be found in the PATH setting at runtime) -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE Use a list of object files found in FILE to specify objects -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface -Wc,FLAG -Xcompiler FLAG pass linker-specific FLAG directly to the compiler -Wl,FLAG -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) All other options (arguments beginning with \`-') are ignored. Every other argument is treated as a filename. Files ending in \`.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in \`.la', then a libtool library is created, only library objects (\`.lo' files) may be specified, and \`-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created using \`ar' and \`ranlib', or on Windows using \`lib'. If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode \`$opt_mode'" ;; esac echo $ECHO "Try \`$progname --help' for more information about other modes." } # Now that we've collected a possible --mode arg, show help if necessary if $opt_help; then if test "$opt_help" = :; then func_mode_help else { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do func_mode_help done } | sed -n '1p; 2,$s/^Usage:/ or: /p' { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do echo func_mode_help done } | sed '1d /^When reporting/,/^Report/{ H d } $x /information about other modes/d /more detailed .*MODE/d s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' fi exit $? fi # func_mode_execute arg... func_mode_execute () { $opt_debug # The first argument is the command name. cmd="$nonopt" test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $opt_dlopen; do test -f "$file" \ || func_fatal_help "\`$file' is not a file" dir= case $file in *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "\`$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "\`$file' was not linked with \`-export-dynamic'" continue fi func_dirname "$file" "" "." dir="$func_dirname_result" if test -f "$dir/$objdir/$dlname"; then func_append dir "/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir="$func_dirname_result" ;; *) func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir="$absdir" # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic="$magic" # Check if any of the arguments is a wrapper script. args= for file do case $file in -* | *.la | *.lo ) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file="$progdir/$program" elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file="$progdir/$program" fi ;; esac # Quote arguments (to preserve shell metacharacters). func_append_quoted args "$file" done if test "X$opt_dry_run" = Xfalse; then if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd="\$cmd$args" else # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" echo "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS fi } test "$opt_mode" = execute && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $opt_debug libs= libdirs= admincmds= for opt in "$nonopt" ${1+"$@"} do if test -d "$opt"; then func_append libdirs " $opt" elif test -f "$opt"; then if func_lalib_unsafe_p "$opt"; then func_append libs " $opt" else func_warning "\`$opt' is not a valid libtool archive" fi else func_fatal_error "invalid argument \`$opt'" fi done if test -n "$libs"; then if test -n "$lt_sysroot"; then sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" else sysroot_cmd= fi # Remove sysroot references if $opt_dry_run; then for lib in $libs; do echo "removing references to $lt_sysroot and \`=' prefixes from $lib" done else tmpdir=`func_mktempdir` for lib in $libs; do sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ > $tmpdir/tmp-la mv -f $tmpdir/tmp-la $lib done ${RM}r "$tmpdir" fi fi if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || func_append admincmds " $cmds" fi done fi # Exit here if they wanted silent mode. $opt_silent && exit $EXIT_SUCCESS if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then echo "----------------------------------------------------------------------" echo "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done echo echo "If you ever happen to want to link against installed libraries" echo "in a given directory, LIBDIR, you must either use libtool, and" echo "specify the full pathname of the library, or use the \`-LLIBDIR'" echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then echo " - add LIBDIR to the \`$shlibpath_var' environment variable" echo " during execution" fi if test -n "$runpath_var"; then echo " - add LIBDIR to the \`$runpath_var' environment variable" echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the \`$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" fi echo echo "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" echo "pages." ;; *) echo "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac echo "----------------------------------------------------------------------" fi exit $EXIT_SUCCESS } test "$opt_mode" = finish && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $opt_debug # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || # Allow the use of GNU shtool's install command. case $nonopt in *shtool*) :;; *) false;; esac; then # Aesthetically quote it. func_quote_for_eval "$nonopt" install_prog="$func_quote_for_eval_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_for_eval "$arg" func_append install_prog "$func_quote_for_eval_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; *) install_cp=false ;; esac # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=no stripme= no_mode=: for arg do arg2= if test -n "$dest"; then func_append files " $dest" dest=$arg continue fi case $arg in -d) isdir=yes ;; -f) if $install_cp; then :; else prev=$arg fi ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then if test "x$prev" = x-m && test -n "$install_override_mode"; then arg2=$install_override_mode no_mode=false fi prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_for_eval "$arg" func_append install_prog " $func_quote_for_eval_result" if test -n "$arg2"; then func_quote_for_eval "$arg2" fi func_append install_shared_prog " $func_quote_for_eval_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the \`$prev' option requires an argument" if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else func_quote_for_eval "$install_override_mode" func_append install_shared_prog " -m $func_quote_for_eval_result" fi fi if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=yes if test "$isdir" = yes; then destdir="$dest" destname= else func_dirname_and_basename "$dest" "" "." destdir="$func_dirname_result" destname="$func_basename_result" # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "\`$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "\`$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic="$magic" staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. func_append staticlibs " $file" ;; *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "\`$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) func_append current_libdirs " $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) func_append future_libdirs " $libdir" ;; esac fi func_dirname "$file" "/" "" dir="$func_dirname_result" func_append dir "$objdir" if test -n "$relink_command"; then # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi func_warning "relinking \`$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname="$1" shift srcname="$realname" test -n "$relink_command" && srcname="$realname"T # Install the shared library and build the symlinks. func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme="$stripme" case $host_os in cygwin* | mingw* | pw32* | cegcc*) case $realname in *.dll.a) tstripme="" ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try `ln -sf' first, because the `ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib="$destdir/$realname" func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name="$func_basename_result" instname="$dir/$name"i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && func_append staticlibs " $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile="$destdir/$destname" else func_basename "$file" destfile="$func_basename_result" destfile="$destdir/$destfile" fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest="$destfile" destfile= ;; *) func_fatal_help "cannot copy a libtool object to \`$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test "$build_old_libs" = yes; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile="$destdir/$destname" else func_basename "$file" destfile="$func_basename_result" destfile="$destdir/$destfile" fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext="" case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=".exe" fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script \`$wrapper'" finalize=yes for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test if test -n "$libdir" && test ! -f "$libfile"; then func_warning "\`$lib' has not been installed in \`$libdir'" finalize=no fi done relink_command= func_source "$wrapper" outputname= if test "$fast_install" = no && test -n "$relink_command"; then $opt_dry_run || { if test "$finalize" = yes; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file="$func_basename_result" outputname="$tmpdir/$file" # Replace the output file specification. relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_silent || { func_quote_for_expand "$relink_command" eval "func_echo $func_quote_for_expand_result" } if eval "$relink_command"; then : else func_error "error: relink \`$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file="$outputname" else func_warning "cannot relink \`$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name="$func_basename_result" # Set up the ranlib parameters. oldlib="$destdir/$name" func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $tool_oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run \`$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test "$opt_mode" = install && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $opt_debug my_outputname="$1" my_originator="$2" my_pic_p="${3-no}" my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms="${my_outputname}S.c" else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist="$output_objdir/${my_outputname}.nm" func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif #if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) /* DATA imports from DLLs on WIN32 con't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined(__osf__) /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif /* External symbol declarations for the compiler. */\ " if test "$dlself" = yes; then func_verbose "generating symbol list for \`$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` for progfile in $progfiles; do func_to_tool_file "$progfile" func_convert_file_msys_to_w32 func_verbose "extracting global C symbols from \`$func_to_tool_file_result'" $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols="$output_objdir/$outputname.exp" $opt_dry_run || { $RM $export_symbols eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from \`$dlprefile'" func_basename "$dlprefile" name="$func_basename_result" case $host in *cygwin* | *mingw* | *cegcc* ) # if an import library, we need to obtain dlname if func_win32_import_lib_p "$dlprefile"; then func_tr_sh "$dlprefile" eval "curr_lafile=\$libfile_$func_tr_sh_result" dlprefile_dlbasename="" if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then # Use subshell, to avoid clobbering current variable values dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` if test -n "$dlprefile_dlname" ; then func_basename "$dlprefile_dlname" dlprefile_dlbasename="$func_basename_result" else # no lafile. user explicitly requested -dlpreopen . $sharedlib_from_linklib_cmd "$dlprefile" dlprefile_dlbasename=$sharedlib_from_linklib_result fi fi $opt_dry_run || { if test -n "$dlprefile_dlbasename" ; then eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' else func_warning "Could not compute DLL name from $name" eval '$ECHO ": $name " >> "$nlist"' fi func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" } else # not an import lib $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } fi ;; *) $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } ;; esac done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi echo >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[]; LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = {\ { \"$my_originator\", (void *) 0 }," case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac echo >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) if test "X$my_pic_p" != Xno; then pic_flag_for_symtable=" $pic_flag" fi ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) func_append symtab_cflags " $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' # Transform the symbol file into the correct name. symfileobj="$output_objdir/${my_outputname}S.$objext" case $host in *cygwin* | *mingw* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for \`$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` fi } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. # Despite the name, also deal with 64 bit binaries. func_win32_libid () { $opt_debug win32_libid_type="unknown" win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then func_to_tool_file "$1" func_convert_file_msys_to_w32 win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | $SED -n -e ' 1,100{ / I /{ s,.*,import, p q } }'` case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_cygming_dll_for_implib ARG # # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib () { $opt_debug sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` } # func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs # # The is the core of a fallback implementation of a # platform-specific function to extract the name of the # DLL associated with the specified import library LIBNAME. # # SECTION_NAME is either .idata$6 or .idata$7, depending # on the platform and compiler that created the implib. # # Echos the name of the DLL associated with the # specified import library. func_cygming_dll_for_implib_fallback_core () { $opt_debug match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` $OBJDUMP -s --section "$1" "$2" 2>/dev/null | $SED '/^Contents of section '"$match_literal"':/{ # Place marker at beginning of archive member dllname section s/.*/====MARK====/ p d } # These lines can sometimes be longer than 43 characters, but # are always uninteresting /:[ ]*file format pe[i]\{,1\}-/d /^In archive [^:]*:/d # Ensure marker is printed /^====MARK====/p # Remove all lines with less than 43 characters /^.\{43\}/!d # From remaining lines, remove first 43 characters s/^.\{43\}//' | $SED -n ' # Join marker and all lines until next marker into a single line /^====MARK====/ b para H $ b para b :para x s/\n//g # Remove the marker s/^====MARK====// # Remove trailing dots and whitespace s/[\. \t]*$// # Print /./p' | # we now have a list, one entry per line, of the stringified # contents of the appropriate section of all members of the # archive which possess that section. Heuristic: eliminate # all those which have a first or second character that is # a '.' (that is, objdump's representation of an unprintable # character.) This should work for all archives with less than # 0x302f exports -- but will fail for DLLs whose name actually # begins with a literal '.' or a single character followed by # a '.'. # # Of those that remain, print the first one. $SED -e '/^\./d;/^.\./d;q' } # func_cygming_gnu_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is a GNU/binutils-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_gnu_implib_p () { $opt_debug func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` test -n "$func_cygming_gnu_implib_tmp" } # func_cygming_ms_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is an MS-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_ms_implib_p () { $opt_debug func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` test -n "$func_cygming_ms_implib_tmp" } # func_cygming_dll_for_implib_fallback ARG # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # # This fallback implementation is for use when $DLLTOOL # does not support the --identify-strict option. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib_fallback () { $opt_debug if func_cygming_gnu_implib_p "$1" ; then # binutils import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` elif func_cygming_ms_implib_p "$1" ; then # ms-generated import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` else # unknown sharedlib_from_linklib_result="" fi } # func_extract_an_archive dir oldlib func_extract_an_archive () { $opt_debug f_ex_an_ar_dir="$1"; shift f_ex_an_ar_oldlib="$1" if test "$lock_old_archive_extraction" = yes; then lockfile=$f_ex_an_ar_oldlib.lock until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done fi func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ 'stat=$?; rm -f "$lockfile"; exit $stat' if test "$lock_old_archive_extraction" = yes; then $opt_dry_run || rm -f "$lockfile" fi if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $opt_debug my_gentop="$1"; shift my_oldlibs=${1+"$@"} my_oldobjs="" my_xlib="" my_xabs="" my_xdir="" for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib="$func_basename_result" my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir="$my_gentop/$my_xlib_u" func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` darwin_base_archive=`basename "$darwin_archive"` darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches ; do func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" func_extract_an_archive "`pwd`" "${darwin_base_archive}" cd "$darwin_curdir" $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` done func_extract_archives_result="$my_oldobjs" } # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory in which it is stored is # the $objdir directory. This is a cygwin/mingw-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=${1-no} $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` $ECHO "\ # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } ECHO=\"$qECHO\" fi # Very basic option parsing. These options are (a) specific to # the libtool wrapper, (b) are identical between the wrapper # /script/ and the wrapper /executable/ which is used only on # windows platforms, and (c) all begin with the string "--lt-" # (application programs are unlikely to have options which match # this pattern). # # There are only two supported options: --lt-debug and # --lt-dump-script. There is, deliberately, no --lt-help. # # The first argument to this parsing function should be the # script's $0 value, followed by "$@". lt_option_debug= func_parse_lt_options () { lt_script_arg0=\$0 shift for lt_opt do case \"\$lt_opt\" in --lt-debug) lt_option_debug=1 ;; --lt-dump-script) lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` cat \"\$lt_dump_D/\$lt_dump_F\" exit 0 ;; --lt-*) \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 exit 1 ;; esac done # Print the debug banner immediately: if test -n \"\$lt_option_debug\"; then echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2 fi } # Used when --lt-debug. Prints its arguments to stdout # (redirection is the responsibility of the caller) func_lt_dump_args () { lt_dump_args_N=1; for lt_arg do \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\" lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` done } # Core function for launching the target application func_exec_program_core () { " case $host in # Backslashes separate directories on plain windows *-*-mingw | *-*-os2* | *-cegcc*) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 } # A function to encapsulate launching the target application # Strips options in the --lt-* namespace from \$@ and # launches target application with the remaining arguments. func_exec_program () { case \" \$* \" in *\\ --lt-*) for lt_wr_arg do case \$lt_wr_arg in --lt-*) ;; *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; esac shift done ;; esac func_exec_program_core \${1+\"\$@\"} } # Parse options func_parse_lt_options \"\$0\" \${1+\"\$@\"} # Find the directory that this script lives in. thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` done # Usually 'no', except on cygwin/mingw when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test "$fast_install" = yes; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else $ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # fixup the dll searchpath if we need to. # # Fix the DLL searchpath if we need to. Do this before prepending # to shlibpath, because on Windows, both are PATH and uninstalled # libraries must come first. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi # Export our shlibpath_var if we have one. if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` export $shlibpath_var " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. func_exec_program \${1+\"\$@\"} fi else # The program doesn't exist. \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #ifdef _MSC_VER # include # include # include #else # include # include # ifdef __CYGWIN__ # include # endif #endif #include #include #include #include #include #include #include #include /* declarations of non-ANSI functions */ #if defined(__MINGW32__) # ifdef __STRICT_ANSI__ int _putenv (const char *); # endif #elif defined(__CYGWIN__) # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif /* #elif defined (other platforms) ... */ #endif /* portability defines, excluding path handling macros */ #if defined(_MSC_VER) # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv # define S_IXUSR _S_IEXEC # ifndef _INTPTR_T_DEFINED # define _INTPTR_T_DEFINED # define intptr_t int # endif #elif defined(__MINGW32__) # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv #elif defined(__CYGWIN__) # define HAVE_SETENV # define FOPEN_WB "wb" /* #elif defined (other platforms) ... */ #endif #if defined(PATH_MAX) # define LT_PATHMAX PATH_MAX #elif defined(MAXPATHLEN) # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif /* path handling portability macros */ #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ defined (__OS2__) # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free ((void *) stale); stale = 0; } \ } while (0) #if defined(LT_DEBUGWRAPPER) static int lt_debug = 1; #else static int lt_debug = 0; #endif const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_debugprintf (const char *file, int line, const char *fmt, ...); void lt_fatal (const char *file, int line, const char *message, ...); static const char *nonnull (const char *s); static const char *nonempty (const char *s); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); char **prepare_spawn (char **argv); void lt_dump_script (FILE *f); EOF cat <= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", nonempty (path)); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; int tmp_len; char *concat_name; lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", nonempty (wrapper)); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined (HAVE_DOS_BASED_FILE_SYSTEM) if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined (HAVE_DOS_BASED_FILE_SYSTEM) } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = q - p; p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { lt_debugprintf (__FILE__, __LINE__, "checking path component for symlinks: %s\n", tmp_pathspec); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { lt_fatal (__FILE__, __LINE__, "error accessing file \"%s\": %s", tmp_pathspec, nonnull (strerror (errno))); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal (__FILE__, __LINE__, "could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (strcmp (str, pat) == 0) *str = '\0'; } return str; } void lt_debugprintf (const char *file, int line, const char *fmt, ...) { va_list args; if (lt_debug) { (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } } static void lt_error_core (int exit_status, const char *file, int line, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *file, int line, const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); va_end (ap); } static const char * nonnull (const char *s) { return s ? s : "(null)"; } static const char * nonempty (const char *s) { return (s && !*s) ? "(empty)" : nonnull (s); } void lt_setenv (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_setenv) setting '%s' to '%s'\n", nonnull (name), nonnull (value)); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else int len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { int orig_value_len = strlen (orig_value); int add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } void lt_update_exe_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ int len = strlen (new_value); while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[len-1] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF case $host_os in mingw*) cat <<"EOF" /* Prepares an argument vector before calling spawn(). Note that spawn() does not by itself call the command interpreter (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&v); v.dwPlatformId == VER_PLATFORM_WIN32_NT; }) ? "cmd.exe" : "command.com"). Instead it simply concatenates the arguments, separated by ' ', and calls CreateProcess(). We must quote the arguments since Win32 CreateProcess() interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a special way: - Space and tab are interpreted as delimiters. They are not treated as delimiters if they are surrounded by double quotes: "...". - Unescaped double quotes are removed from the input. Their only effect is that within double quotes, space and tab are treated like normal characters. - Backslashes not followed by double quotes are not special. - But 2*n+1 backslashes followed by a double quote become n backslashes followed by a double quote (n >= 0): \" -> " \\\" -> \" \\\\\" -> \\" */ #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" char ** prepare_spawn (char **argv) { size_t argc; char **new_argv; size_t i; /* Count number of arguments. */ for (argc = 0; argv[argc] != NULL; argc++) ; /* Allocate new argument vector. */ new_argv = XMALLOC (char *, argc + 1); /* Put quoted arguments into the new argument vector. */ for (i = 0; i < argc; i++) { const char *string = argv[i]; if (string[0] == '\0') new_argv[i] = xstrdup ("\"\""); else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) { int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); size_t length; unsigned int backslashes; const char *s; char *quoted_string; char *p; length = 0; backslashes = 0; if (quote_around) length++; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') length += backslashes + 1; length++; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) length += backslashes + 1; quoted_string = XMALLOC (char, length + 1); p = quoted_string; backslashes = 0; if (quote_around) *p++ = '"'; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') { unsigned int j; for (j = backslashes + 1; j > 0; j--) *p++ = '\\'; } *p++ = c; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) { unsigned int j; for (j = backslashes; j > 0; j--) *p++ = '\\'; *p++ = '"'; } *p = '\0'; new_argv[i] = quoted_string; } else new_argv[i] = (char *) string; } new_argv[argc] = NULL; return new_argv; } EOF ;; esac cat <<"EOF" void lt_dump_script (FILE* f) { EOF func_emit_wrapper yes | $SED -n -e ' s/^\(.\{79\}\)\(..*\)/\1\ \2/ h s/\([\\"]\)/\\\1/g s/$/\\n/ s/\([^\n]*\).*/ fputs ("\1", f);/p g D' cat <<"EOF" } EOF } # end: func_emit_cwrapperexe_src # func_win32_import_lib_p ARG # True if ARG is an import lib, as indicated by $file_magic_cmd func_win32_import_lib_p () { $opt_debug case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in *import*) : ;; *) false ;; esac } # func_mode_link arg... func_mode_link () { $opt_debug case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # which system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll which has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no bindir= dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=no prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= thread_safe=no vinfo= vinfo_number=no weak_libs= single_module="${wl}-single_module" func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test "$build_libtool_libs" != yes && \ func_fatal_configuration "can not build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg="$1" shift func_quote_for_eval "$arg" qarg=$func_quote_for_eval_unquoted_result func_append libtool_args " $func_quote_for_eval_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in bindir) bindir="$arg" prev= continue ;; dlfiles|dlprefiles) if test "$preload" = no; then # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=yes fi case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test "$dlself" = no; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test "$prev" = dlprefiles; then dlself=yes elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test "$prev" = dlfiles; then func_append dlfiles " $arg" else func_append dlprefiles " $arg" fi prev= continue ;; esac ;; expsyms) export_symbols="$arg" test -f "$arg" \ || func_fatal_error "symbol file \`$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex="$arg" prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) func_append deplibs " $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir="$arg" prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # func_append moreargs " $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test "$pic_object" = none && test "$non_pic_object" = none; then func_fatal_error "cannot find name of object for \`$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" if test "$pic_object" != none; then # Prepend the subdirectory the object is found in. pic_object="$xdir$pic_object" if test "$prev" = dlfiles; then if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test "$prev" = dlprefiles; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg="$pic_object" fi # Non-PIC object. if test "$non_pic_object" != none; then # Prepend the subdirectory the object is found in. non_pic_object="$xdir$non_pic_object" # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test "$pic_object" = none ; then arg="$non_pic_object" fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object="$pic_object" func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "\`$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file \`$arg' does not exist" fi arg=$save_arg prev= continue ;; precious_regex) precious_files_regex="$arg" prev= continue ;; release) release="-$arg" prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac if test "$prev" = rpath; then case "$rpath " in *" $arg "*) ;; *) func_append rpath " $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) func_append xrpath " $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds="$arg" prev= continue ;; weak) func_append weak_libs " $arg" prev= continue ;; xcclinker) func_append linker_flags " $qarg" func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) func_append linker_flags " $qarg" func_append compiler_flags " $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg="$arg" case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "\`-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -bindir) prev=bindir continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test "X$arg" = "X-export-symbols"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname "-L" '' "$arg" if test -z "$func_stripname_result"; then if test "$#" -gt 0; then func_fatal_error "require no space between \`-L' and \`$1'" else func_fatal_error "need path for \`-L' option" fi fi func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of \`$dir'" dir="$absdir" ;; esac case "$deplibs " in *" -L$dir "* | *" $arg "*) # Will only happen for absolute or sysroot arguments ;; *) # Preserve sysroot, but never include relative directories case $dir in [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; *) func_append deplibs " -L$dir" ;; esac func_append lib_search_path " $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) func_append dllsearchpath ":$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac continue ;; -l*) if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test "X$arg" = "X-lc" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. test "X$arg" = "X-lc" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework func_append deplibs " System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test "X$arg" = "X-lc" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test "X$arg" = "X-lc" && continue ;; esac elif test "X$arg" = "X-lc_r"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi func_append deplibs " $arg" continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. -model|-arch|-isysroot|--sysroot) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" prev=xcompiler continue ;; -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) func_append new_inherited_linker_flags " $arg" ;; esac continue ;; -multi_module) single_module="${wl}-multi_module" continue ;; -no-fast-install) fast_install=no continue ;; -no-install) case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) # The PATH hackery in wrapper scripts is required on Windows # and Darwin in order for the loader to find any dlls it needs. func_warning "\`-no-install' is ignored for $host" func_warning "assuming \`-no-fast-install' instead" fast_install=no ;; *) no_install=yes ;; esac continue ;; -no-undefined) allow_undefined=no continue ;; -objectlist) prev=objectlist continue ;; -o) prev=output ;; -precious-files-regex) prev=precious_regex continue ;; -release) prev=release continue ;; -rpath) prev=rpath continue ;; -R) prev=xrpath continue ;; -R*) func_stripname '-R' '' "$arg" dir=$func_stripname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; =*) func_stripname '=' '' "$dir" dir=$lt_sysroot$func_stripname_result ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac continue ;; -shared) # The effects of -shared are defined in a previous loop. continue ;; -shrext) prev=shrext continue ;; -static | -static-libtool-libs) # The effects of -static are defined in a previous loop. # We used to do the same as -all-static on platforms that # didn't have a PIC flag, but the assumption that the effects # would be equivalent was wrong. It would break on at least # Digital Unix and AIX. continue ;; -thread-safe) thread_safe=yes continue ;; -version-info) prev=vinfo continue ;; -version-number) prev=vinfo vinfo_number=yes continue ;; -weak) prev=weak continue ;; -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result arg= save_ifs="$IFS"; IFS=',' for flag in $args; do IFS="$save_ifs" func_quote_for_eval "$flag" func_append arg " $func_quote_for_eval_result" func_append compiler_flags " $func_quote_for_eval_result" done IFS="$save_ifs" func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Wl,*) func_stripname '-Wl,' '' "$arg" args=$func_stripname_result arg= save_ifs="$IFS"; IFS=',' for flag in $args; do IFS="$save_ifs" func_quote_for_eval "$flag" func_append arg " $wl$func_quote_for_eval_result" func_append compiler_flags " $wl$func_quote_for_eval_result" func_append linker_flags " $func_quote_for_eval_result" done IFS="$save_ifs" func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Xcompiler) prev=xcompiler continue ;; -Xlinker) prev=xlinker continue ;; -XCClinker) prev=xcclinker continue ;; # -msg_* for osf cc -msg_*) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" ;; # Flags to be passed through unchanged, with rationale: # -64, -mips[0-9] enable 64-bit mode for the SGI compiler # -r[0-9][0-9]* specify processor for the SGI compiler # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler # +DA*, +DD* enable 64-bit mode for the HP compiler # -q* compiler args for the IBM compiler # -m*, -t[45]*, -txscale* architecture-specific flags for GCC # -F/path path to uninstalled frameworks, gcc on darwin # -p, -pg, --coverage, -fprofile-* profiling flags for GCC # @file GCC response files # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ -O*|-flto*|-fwhopr*|-fuse-linker-plugin) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" func_append compile_command " $arg" func_append finalize_command " $arg" func_append compiler_flags " $arg" continue ;; # Some other compiler flag. -* | +*) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" ;; *.$objext) # A standard object. func_append objs " $arg" ;; *.lo) # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test "$pic_object" = none && test "$non_pic_object" = none; then func_fatal_error "cannot find name of object for \`$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" if test "$pic_object" != none; then # Prepend the subdirectory the object is found in. pic_object="$xdir$pic_object" if test "$prev" = dlfiles; then if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test "$prev" = dlprefiles; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg="$pic_object" fi # Non-PIC object. if test "$non_pic_object" != none; then # Prepend the subdirectory the object is found in. non_pic_object="$xdir$non_pic_object" # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test "$pic_object" = none ; then arg="$non_pic_object" fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object="$pic_object" func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "\`$arg' is not a valid libtool object" fi fi ;; *.$libext) # An archive. func_append deplibs " $arg" func_append old_deplibs " $arg" continue ;; *.la) # A libtool-controlled library. func_resolve_sysroot "$arg" if test "$prev" = dlfiles; then # This library was specified with -dlopen. func_append dlfiles " $func_resolve_sysroot_result" prev= elif test "$prev" = dlprefiles; then # The library was specified with -dlpreopen. func_append dlprefiles " $func_resolve_sysroot_result" prev= else func_append deplibs " $func_resolve_sysroot_result" fi continue ;; # Some other compiler argument. *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" ;; esac # arg # Now actually substitute the argument into the commands. if test -n "$arg"; then func_append compile_command " $arg" func_append finalize_command " $arg" fi done # argument parsing loop test -n "$prev" && \ func_fatal_help "the \`$prevarg' option requires an argument" if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" func_append compile_command " $arg" func_append finalize_command " $arg" fi oldlibs= # calculate the name of the file, without its directory func_basename "$output" outputname="$func_basename_result" libobjs_save="$libobjs" if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" func_dirname "$output" "/" "" output_objdir="$func_dirname_result$objdir" func_to_tool_file "$output_objdir/" tool_output_objdir=$func_to_tool_file_result # Create the object directory. func_mkdir_p "$output_objdir" # Determine the type of output case $output in "") func_fatal_help "you must specify an output file" ;; *.$libext) linkmode=oldlib ;; *.lo | *.$objext) linkmode=obj ;; *.la) linkmode=lib ;; *) linkmode=prog ;; # Anything else should be a program. esac specialdeplibs= libs= # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do if $opt_preserve_dup_deps ; then case "$libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append libs " $deplib" done if test "$linkmode" = lib; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps # $postdeps and mark them as special (i.e., whose duplicates are # not to be eliminated). pre_post_deps= if $opt_duplicate_compiler_generated_deps; then for pre_post_dep in $predeps $postdeps; do case "$pre_post_deps " in *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; esac func_append pre_post_deps " $pre_post_dep" done fi pre_post_deps= fi deplibs= newdependency_libs= newlib_search_path= need_relink=no # whether we're linking any uninstalled libtool libraries notinst_deplibs= # not-installed libtool libraries notinst_path= # paths that contain not-installed libtool libraries case $linkmode in lib) passes="conv dlpreopen link" for file in $dlfiles $dlprefiles; do case $file in *.la) ;; *) func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" ;; esac done ;; prog) compile_deplibs= finalize_deplibs= alldeplibs=no newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" ;; *) passes="conv" ;; esac for pass in $passes; do # The preopen pass in lib mode reverses $deplibs; put it back here # so that -L comes before libs that need it for instance... if test "$linkmode,$pass" = "lib,link"; then ## FIXME: Find the place where the list is rebuilt in the wrong ## order, and fix it there properly tmp_deplibs= for deplib in $deplibs; do tmp_deplibs="$deplib $tmp_deplibs" done deplibs="$tmp_deplibs" fi if test "$linkmode,$pass" = "lib,link" || test "$linkmode,$pass" = "prog,scan"; then libs="$deplibs" deplibs= fi if test "$linkmode" = prog; then case $pass in dlopen) libs="$dlfiles" ;; dlpreopen) libs="$dlprefiles" ;; link) libs="$deplibs %DEPLIBS% $dependency_libs" ;; esac fi if test "$linkmode,$pass" = "lib,dlpreopen"; then # Collect and forward deplibs of preopened libtool libs for lib in $dlprefiles; do # Ignore non-libtool-libs dependency_libs= func_resolve_sysroot "$lib" case $lib in *.la) func_source "$func_resolve_sysroot_result" ;; esac # Collect preopened libtool deplibs, except any this library # has declared as weak libs for deplib in $dependency_libs; do func_basename "$deplib" deplib_base=$func_basename_result case " $weak_libs " in *" $deplib_base "*) ;; *) func_append deplibs " $deplib" ;; esac done done libs="$dlprefiles" fi if test "$pass" = dlopen; then # Collect dlpreopened libraries save_deplibs="$deplibs" deplibs= fi for deplib in $libs; do lib= found=no case $deplib in -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append compiler_flags " $deplib" if test "$linkmode" = lib ; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -l*) if test "$linkmode" != lib && test "$linkmode" != prog; then func_warning "\`-l' is ignored for archives/objects" continue fi func_stripname '-l' '' "$deplib" name=$func_stripname_result if test "$linkmode" = lib; then searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" else searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" fi for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib="$searchdir/lib${name}${search_ext}" if test -f "$lib"; then if test "$search_ext" = ".la"; then found=yes else found=no fi break 2 fi done done if test "$found" != yes; then # deplib doesn't seem to be a libtool library if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" fi continue else # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then case " $predeps $postdeps " in *" $deplib "*) if func_lalib_p "$lib"; then library_names= old_library= func_source "$lib" for l in $old_library $library_names; do ll="$l" done if test "X$ll" = "X$old_library" ; then # only static version available found=no func_dirname "$lib" "" "." ladir="$func_dirname_result" lib=$ladir/$old_library if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" fi continue fi fi ;; *) ;; esac fi fi ;; # -l *.ltframework) if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" if test "$linkmode" = lib ; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -L*) case $linkmode in lib) deplibs="$deplib $deplibs" test "$pass" = conv && continue newdependency_libs="$deplib $newdependency_libs" func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; prog) if test "$pass" = conv; then deplibs="$deplib $deplibs" continue fi if test "$pass" = scan; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; *) func_warning "\`-L' is ignored for archives/objects" ;; esac # linkmode continue ;; # -L -R*) if test "$pass" = link; then func_stripname '-R' '' "$deplib" func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # Make sure the xrpath contains only unique directories. case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac fi deplibs="$deplib $deplibs" continue ;; *.la) func_resolve_sysroot "$deplib" lib=$func_resolve_sysroot_result ;; *.$libext) if test "$pass" = conv; then deplibs="$deplib $deplibs" continue fi case $linkmode in lib) # Linking convenience modules into shared libraries is allowed, # but linking other static libraries is non-portable. case " $dlpreconveniencelibs " in *" $deplib "*) ;; *) valid_a_lib=no case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then valid_a_lib=yes fi ;; pass_all) valid_a_lib=yes ;; esac if test "$valid_a_lib" != yes; then echo $ECHO "*** Warning: Trying to link with static lib archive $deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because the file extensions .$libext of this argument makes me believe" echo "*** that it is just a static archive that I should not use here." else echo $ECHO "*** Warning: Linking the shared library $output against the" $ECHO "*** static library $deplib is not portable!" deplibs="$deplib $deplibs" fi ;; esac continue ;; prog) if test "$pass" != link; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi continue ;; esac # linkmode ;; # *.$libext *.lo | *.$objext) if test "$pass" = conv; then deplibs="$deplib $deplibs" elif test "$linkmode" = prog; then if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then # If there is no dlopen support or we're linking statically, # we need to preload. func_append newdlprefiles " $deplib" compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append newdlfiles " $deplib" fi fi continue ;; %DEPLIBS%) alldeplibs=yes continue ;; esac # case $deplib if test "$found" = yes || test -f "$lib"; then : else func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" fi # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$lib" \ || func_fatal_error "\`$lib' is not a valid libtool archive" func_dirname "$lib" "" "." ladir="$func_dirname_result" dlname= dlopen= dlpreopen= libdir= library_names= old_library= inherited_linker_flags= # If the library was installed with an old release of libtool, # it will not redefine variables installed, or shouldnotlink installed=yes shouldnotlink=no avoidtemprpath= # Read the .la file func_source "$lib" # Convert "-framework foo" to "foo.ltframework" if test -n "$inherited_linker_flags"; then tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do case " $new_inherited_linker_flags " in *" $tmp_inherited_linker_flag "*) ;; *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; esac done fi dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` if test "$linkmode,$pass" = "lib,link" || test "$linkmode,$pass" = "prog,scan" || { test "$linkmode" != prog && test "$linkmode" != lib; }; then test -n "$dlopen" && func_append dlfiles " $dlopen" test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" fi if test "$pass" = conv; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then func_fatal_error "cannot find name of link library for \`$lib'" fi # It is a libtool convenience library, so add in its objects. func_append convenience " $ladir/$objdir/$old_library" func_append old_convenience " $ladir/$objdir/$old_library" elif test "$linkmode" != prog && test "$linkmode" != lib; then func_fatal_error "\`$lib' is not a convenience library" fi tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" if $opt_preserve_dup_deps ; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done continue fi # $pass = conv # Get the name of the library we link against. linklib= if test -n "$old_library" && { test "$prefer_static_libs" = yes || test "$prefer_static_libs,$installed" = "built,no"; }; then linklib=$old_library else for l in $old_library $library_names; do linklib="$l" done fi if test -z "$linklib"; then func_fatal_error "cannot find name of link library for \`$lib'" fi # This library was specified with -dlopen. if test "$pass" = dlopen; then if test -z "$libdir"; then func_fatal_error "cannot -dlopen a convenience library: \`$lib'" fi if test -z "$dlname" || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't # bomb out in the load deplibs phase. func_append dlprefiles " $lib $dependency_libs" else func_append newdlfiles " $lib" fi continue fi # $pass = dlopen # We need an absolute path. case $ladir in [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then func_warning "cannot determine absolute directory name of \`$ladir'" func_warning "passing it literally to the linker, although it might fail" abs_ladir="$ladir" fi ;; esac func_basename "$lib" laname="$func_basename_result" # Find the relevant object directory and library name. if test "X$installed" = Xyes; then if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then func_warning "library \`$lib' was moved." dir="$ladir" absdir="$abs_ladir" libdir="$abs_ladir" else dir="$lt_sysroot$libdir" absdir="$lt_sysroot$libdir" fi test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then dir="$ladir" absdir="$abs_ladir" # Remove this search path later func_append notinst_path " $abs_ladir" else dir="$ladir/$objdir" absdir="$abs_ladir/$objdir" # Remove this search path later func_append notinst_path " $abs_ladir" fi fi # $installed = yes func_stripname 'lib' '.la' "$laname" name=$func_stripname_result # This library was specified with -dlpreopen. if test "$pass" = dlpreopen; then if test -z "$libdir" && test "$linkmode" = prog; then func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" fi case "$host" in # special handling for platforms with PE-DLLs. *cygwin* | *mingw* | *cegcc* ) # Linker will automatically link against shared library if both # static and shared are present. Therefore, ensure we extract # symbols from the import library if a shared library is present # (otherwise, the dlopen module name will be incorrect). We do # this by putting the import library name into $newdlprefiles. # We recover the dlopen module name by 'saving' the la file # name in a special purpose variable, and (later) extracting the # dlname from the la file. if test -n "$dlname"; then func_tr_sh "$dir/$linklib" eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" func_append newdlprefiles " $dir/$linklib" else func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" fi ;; * ) # Prefer using a static library (so that no silly _DYNAMIC symbols # are required to link). if test -n "$old_library"; then func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" # Otherwise, use the dlname, so that lt_dlopen finds it. elif test -n "$dlname"; then func_append newdlprefiles " $dir/$dlname" else func_append newdlprefiles " $dir/$linklib" fi ;; esac fi # $pass = dlpreopen if test -z "$libdir"; then # Link the convenience library if test "$linkmode" = lib; then deplibs="$dir/$old_library $deplibs" elif test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else deplibs="$lib $deplibs" # used for prog,scan pass fi continue fi if test "$linkmode" = prog && test "$pass" != link; then func_append newlib_search_path " $ladir" deplibs="$lib $deplibs" linkalldeplibs=no if test "$link_all_deplibs" != no || test -z "$library_names" || test "$build_libtool_libs" = no; then linkalldeplibs=yes fi tmp_libs= for deplib in $dependency_libs; do case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; esac # Need to link against all dependency_libs? if test "$linkalldeplibs" = yes; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi if $opt_preserve_dup_deps ; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done # for deplib continue fi # $linkmode = prog... if test "$linkmode,$pass" = "prog,link"; then if test -n "$library_names" && { { test "$prefer_static_libs" = no || test "$prefer_static_libs,$installed" = "built,yes"; } || test -z "$old_library"; }; then # We need to hardcode the library path if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then # Make sure the rpath contains only unique directories. case "$temp_rpath:" in *"$absdir:"*) ;; *) func_append temp_rpath "$absdir:" ;; esac fi # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi # $linkmode,$pass = prog,link... if test "$alldeplibs" = yes && { test "$deplibs_check_method" = pass_all || { test "$build_libtool_libs" = yes && test -n "$library_names"; }; }; then # We only need to search for static libraries continue fi fi link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs if test "$use_static_libs" = built && test "$installed" = yes; then use_static_libs=no fi if test -n "$library_names" && { test "$use_static_libs" = no || test -z "$old_library"; }; then case $host in *cygwin* | *mingw* | *cegcc*) # No point in relinking DLLs because paths are not encoded func_append notinst_deplibs " $lib" need_relink=no ;; *) if test "$installed" = no; then func_append notinst_deplibs " $lib" need_relink=yes fi ;; esac # This is a shared library # Warn about portability, can't link against -module's on some # systems (darwin). Don't bleat about dlopened modules though! dlopenmodule="" for dlpremoduletest in $dlprefiles; do if test "X$dlpremoduletest" = "X$lib"; then dlopenmodule="$dlpremoduletest" break fi done if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then echo if test "$linkmode" = prog; then $ECHO "*** Warning: Linking the executable $output against the loadable module" else $ECHO "*** Warning: Linking the shared library $output against the loadable module" fi $ECHO "*** $linklib is not portable!" fi if test "$linkmode" = lib && test "$hardcode_into_libs" = yes; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi if test -n "$old_archive_from_expsyms_cmds"; then # figure out the soname set dummy $library_names shift realname="$1" shift libname=`eval "\\$ECHO \"$libname_spec\""` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then soname="$dlname" elif test -n "$soname_spec"; then # bleh windows case $host in *cygwin* | mingw* | *cegcc*) func_arith $current - $age major=$func_arith_result versuffix="-$major" ;; esac eval soname=\"$soname_spec\" else soname="$realname" fi # Make a new name for the extract_expsyms_cmds to use soroot="$soname" func_basename "$soroot" soname="$func_basename_result" func_stripname 'lib' '.dll' "$soname" newlib=libimp-$func_stripname_result.a # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else func_verbose "extracting exported symbol list from \`$soname'" func_execute_cmds "$extract_expsyms_cmds" 'exit $?' fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else func_verbose "generating import library for \`$soname'" func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' fi # make sure the library variables are pointing to the new library dir=$output_objdir linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" if test "$linkmode" = prog || test "$opt_mode" != relink; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) if test "$hardcode_direct" = no; then add="$dir/$linklib" case $host in *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; *-*-sysv4*uw2*) add_dir="-L$dir" ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ *-*-unixware7*) add_dir="-L$dir" ;; *-*-darwin* ) # if the lib is a (non-dlopened) module then we can not # link against it, someone is ignoring the earlier warnings if /usr/bin/file -L $add 2> /dev/null | $GREP ": [^:]* bundle" >/dev/null ; then if test "X$dlopenmodule" != "X$lib"; then $ECHO "*** Warning: lib $linklib is a module, not a shared library" if test -z "$old_library" ; then echo echo "*** And there doesn't seem to be a static archive available" echo "*** The link will probably fail, sorry" else add="$dir/$old_library" fi elif test -n "$old_library"; then add="$dir/$old_library" fi fi esac elif test "$hardcode_minus_L" = no; then case $host in *-*-sunos*) add_shlibpath="$dir" ;; esac add_dir="-L$dir" add="-l$name" elif test "$hardcode_shlibpath_var" = no; then add_shlibpath="$dir" add="-l$name" else lib_linked=no fi ;; relink) if test "$hardcode_direct" = yes && test "$hardcode_direct_absolute" = no; then add="$dir/$linklib" elif test "$hardcode_minus_L" = yes; then add_dir="-L$absdir" # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add="-l$name" elif test "$hardcode_shlibpath_var" = yes; then add_shlibpath="$dir" add="-l$name" else lib_linked=no fi ;; *) lib_linked=no ;; esac if test "$lib_linked" != yes; then func_fatal_configuration "unsupported hardcode properties" fi if test -n "$add_shlibpath"; then case :$compile_shlibpath: in *":$add_shlibpath:"*) ;; *) func_append compile_shlibpath "$add_shlibpath:" ;; esac fi if test "$linkmode" = prog; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" if test "$hardcode_direct" != yes && test "$hardcode_minus_L" != yes && test "$hardcode_shlibpath_var" = yes; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac fi fi fi if test "$linkmode" = prog || test "$opt_mode" = relink; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. if test "$hardcode_direct" = yes && test "$hardcode_direct_absolute" = no; then add="$libdir/$linklib" elif test "$hardcode_minus_L" = yes; then add_dir="-L$libdir" add="-l$name" elif test "$hardcode_shlibpath_var" = yes; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac add="-l$name" elif test "$hardcode_automatic" = yes; then if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib" ; then add="$inst_prefix_dir$libdir/$linklib" else add="$libdir/$linklib" fi else # We cannot seem to hardcode it, guess we'll fake it. add_dir="-L$libdir" # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add="-l$name" fi if test "$linkmode" = prog; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" fi fi elif test "$linkmode" = prog; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. if test "$hardcode_direct" != unsupported; then test -n "$old_library" && linklib="$old_library" compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi elif test "$build_libtool_libs" = yes; then # Not a shared library if test "$deplibs_check_method" != pass_all; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. echo $ECHO "*** Warning: This system can not link to static lib archive $lib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have." if test "$module" = yes; then echo "*** But as you try to build a module library, libtool will still create " echo "*** a static module, that should work as long as the dlopening application" echo "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using \`nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** \`nm' from GNU binutils and a full rebuild may help." fi if test "$build_old_libs" = no; then build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi else deplibs="$dir/$old_library $deplibs" link_static=yes fi fi # link shared/static library? if test "$linkmode" = lib; then if test -n "$dependency_libs" && { test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes || test "$link_static" = yes; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do case $libdir in -R*) func_stripname '-R' '' "$libdir" temp_xrpath=$func_stripname_result case " $xrpath " in *" $temp_xrpath "*) ;; *) func_append xrpath " $temp_xrpath";; esac;; *) func_append temp_deplibs " $libdir";; esac done dependency_libs="$temp_deplibs" fi func_append newlib_search_path " $absdir" # Link against this library test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do newdependency_libs="$deplib $newdependency_libs" case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result";; *) func_resolve_sysroot "$deplib" ;; esac if $opt_preserve_dup_deps ; then case "$tmp_libs " in *" $func_resolve_sysroot_result "*) func_append specialdeplibs " $func_resolve_sysroot_result" ;; esac fi func_append tmp_libs " $func_resolve_sysroot_result" done if test "$link_all_deplibs" != no; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do path= case $deplib in -L*) path="$deplib" ;; *.la) func_resolve_sysroot "$deplib" deplib=$func_resolve_sysroot_result func_dirname "$deplib" "" "." dir=$func_dirname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then func_warning "cannot determine absolute directory name of \`$dir'" absdir="$dir" fi ;; esac if $GREP "^installed=no" $deplib > /dev/null; then case $host in *-*-darwin*) depdepl= eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names" ; then for tmp in $deplibrary_names ; do depdepl=$tmp done if test -f "$absdir/$objdir/$depdepl" ; then depdepl="$absdir/$objdir/$depdepl" darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` if test -z "$darwin_install_name"; then darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` fi func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" func_append linker_flags " -dylib_file ${darwin_install_name}:${depdepl}" path= fi fi ;; *) path="-L$absdir/$objdir" ;; esac else eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "\`$deplib' is not a valid libtool archive" test "$absdir" != "$libdir" && \ func_warning "\`$deplib' seems to be moved" path="-L$absdir" fi ;; esac case " $deplibs " in *" $path "*) ;; *) deplibs="$path $deplibs" ;; esac done fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs if test "$pass" = link; then if test "$linkmode" = "prog"; then compile_deplibs="$new_inherited_linker_flags $compile_deplibs" finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" else compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` fi fi dependency_libs="$newdependency_libs" if test "$pass" = dlpreopen; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi if test "$pass" != dlopen; then if test "$pass" != conv; then # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do case "$lib_search_path " in *" $dir "*) ;; *) func_append lib_search_path " $dir" ;; esac done newlib_search_path= fi if test "$linkmode,$pass" != "prog,link"; then vars="deplibs" else vars="compile_deplibs finalize_deplibs" fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order eval tmp_libs=\"\$$var\" new_libs= for deplib in $tmp_libs; do # FIXME: Pedantically, this is the right thing to do, so # that some nasty dependency loop isn't accidentally # broken: #new_libs="$deplib $new_libs" # Pragmatically, this seems to cause very few problems in # practice: case $deplib in -L*) new_libs="$deplib $new_libs" ;; -R*) ;; *) # And here is the reason: when a library appears more # than once as an explicit dependence of a library, or # is implicitly linked in more than once by the # compiler, it is considered special, and multiple # occurrences thereof are not removed. Compare this # with having the same library being listed as a # dependency of multiple other libraries: in this case, # we know (pedantically, we assume) the library does not # need to be listed more than once, so we keep only the # last copy. This is not always right, but it is rare # enough that we require users that really mean to play # such unportable linking tricks to link the library # using -Wl,-lname, so that libtool does not consider it # for duplicate removal. case " $specialdeplibs " in *" $deplib "*) new_libs="$deplib $new_libs" ;; *) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$deplib $new_libs" ;; esac ;; esac ;; esac done tmp_libs= for deplib in $new_libs; do case $deplib in -L*) case " $tmp_libs " in *" $deplib "*) ;; *) func_append tmp_libs " $deplib" ;; esac ;; *) func_append tmp_libs " $deplib" ;; esac done eval $var=\"$tmp_libs\" done # for var fi # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= for i in $dependency_libs ; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) i="" ;; esac if test -n "$i" ; then func_append tmp_libs " $i" fi done dependency_libs=$tmp_libs done # for pass if test "$linkmode" = prog; then dlfiles="$newdlfiles" fi if test "$linkmode" = prog || test "$linkmode" = lib; then dlprefiles="$newdlprefiles" fi case $linkmode in oldlib) if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then func_warning "\`-dlopen' is ignored for archives" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "\`-l' and \`-L' are ignored for archives" ;; esac test -n "$rpath" && \ func_warning "\`-rpath' is ignored for archives" test -n "$xrpath" && \ func_warning "\`-R' is ignored for archives" test -n "$vinfo" && \ func_warning "\`-version-info/-version-number' is ignored for archives" test -n "$release" && \ func_warning "\`-release' is ignored for archives" test -n "$export_symbols$export_symbols_regex" && \ func_warning "\`-export-symbols' is ignored for archives" # Now set the variables for building old libraries. build_libtool_libs=no oldlibs="$output" func_append objs "$old_deplibs" ;; lib) # Make sure we only generate libraries of the form `libNAME.la'. case $outputname in lib*) func_stripname 'lib' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" ;; *) test "$module" = no && \ func_fatal_help "libtool library \`$output' must begin with \`lib'" if test "$need_lib_prefix" != no; then # Add the "lib" prefix for modules if required func_stripname '' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" else func_stripname '' '.la' "$outputname" libname=$func_stripname_result fi ;; esac if test -n "$objs"; then if test "$deplibs_check_method" != pass_all; then func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" else echo $ECHO "*** Warning: Linking the shared library $output against the non-libtool" $ECHO "*** objects $objs is not portable!" func_append libobjs " $objs" fi fi test "$dlself" != no && \ func_warning "\`-dlopen self' is ignored for libtool libraries" set dummy $rpath shift test "$#" -gt 1 && \ func_warning "ignoring multiple \`-rpath's for a libtool library" install_libdir="$1" oldlibs= if test -z "$rpath"; then if test "$build_libtool_libs" = yes; then # Building a libtool convenience library. # Some compilers have problems with a `.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" build_libtool_libs=convenience build_old_libs=yes fi test -n "$vinfo" && \ func_warning "\`-version-info/-version-number' is ignored for convenience libraries" test -n "$release" && \ func_warning "\`-release' is ignored for convenience libraries" else # Parse the version information argument. save_ifs="$IFS"; IFS=':' set dummy $vinfo 0 0 0 shift IFS="$save_ifs" test -n "$7" && \ func_fatal_help "too many parameters to \`-version-info'" # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts # to make the code below a bit more comprehensible case $vinfo_number in yes) number_major="$1" number_minor="$2" number_revision="$3" # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix # which has an extra 1 added just for fun # case $version_type in # correct linux to gnu/linux during the next big refactor darwin|linux|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result age="$number_minor" revision="$number_revision" ;; freebsd-aout|freebsd-elf|qnx|sunos) current="$number_major" revision="$number_minor" age="0" ;; irix|nonstopux) func_arith $number_major + $number_minor current=$func_arith_result age="$number_minor" revision="$number_minor" lt_irix_increment=no ;; esac ;; no) current="$1" revision="$2" age="$3" ;; esac # Check that each of the things are valid numbers. case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "CURRENT \`$current' must be a nonnegative integer" func_fatal_error "\`$vinfo' is not valid version information" ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "REVISION \`$revision' must be a nonnegative integer" func_fatal_error "\`$vinfo' is not valid version information" ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "AGE \`$age' must be a nonnegative integer" func_fatal_error "\`$vinfo' is not valid version information" ;; esac if test "$age" -gt "$current"; then func_error "AGE \`$age' is greater than the current interface number \`$current'" func_fatal_error "\`$vinfo' is not valid version information" fi # Calculate the version variables. major= versuffix= verstring= case $version_type in none) ;; darwin) # Like Linux, but with the current version available in # verstring for coding it into the library header func_arith $current - $age major=.$func_arith_result versuffix="$major.$age.$revision" # Darwin ld doesn't like 0 for these options... func_arith $current + 1 minor_current=$func_arith_result xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" ;; freebsd-aout) major=".$current" versuffix=".$current.$revision"; ;; freebsd-elf) major=".$current" versuffix=".$current" ;; irix | nonstopux) if test "X$lt_irix_increment" = "Xno"; then func_arith $current - $age else func_arith $current - $age + 1 fi major=$func_arith_result case $version_type in nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac verstring="$verstring_prefix$major.$revision" # Add in all the interfaces that we are compatible with. loop=$revision while test "$loop" -ne 0; do func_arith $revision - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring="$verstring_prefix$major.$iface:$verstring" done # Before this point, $major must not contain `.'. major=.$major versuffix="$major.$revision" ;; linux) # correct to gnu/linux during the next big refactor func_arith $current - $age major=.$func_arith_result versuffix="$major.$age.$revision" ;; osf) func_arith $current - $age major=.$func_arith_result versuffix=".$current.$age.$revision" verstring="$current.$age.$revision" # Add in all the interfaces that we are compatible with. loop=$age while test "$loop" -ne 0; do func_arith $current - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring="$verstring:${iface}.0" done # Make executables depend on our current version. func_append verstring ":${current}.0" ;; qnx) major=".$current" versuffix=".$current" ;; sunos) major=".$current" versuffix=".$current.$revision" ;; windows) # Use '-' rather than '.', since we only want one # extension on DOS 8.3 filesystems. func_arith $current - $age major=$func_arith_result versuffix="-$major" ;; *) func_fatal_configuration "unknown library version type \`$version_type'" ;; esac # Clear the version info if we defaulted, and they specified a release. if test -z "$vinfo" && test -n "$release"; then major= case $version_type in darwin) # we can't check for "0.0" in archive_cmds due to quoting # problems, so we reset it completely verstring= ;; *) verstring="0.0" ;; esac if test "$need_version" = no; then versuffix= else versuffix=".0.0" fi fi # Remove version info from name if versioning should be avoided if test "$avoid_version" = yes && test "$need_version" = no; then major= versuffix= verstring="" fi # Check to see if the archive will have undefined symbols. if test "$allow_undefined" = yes; then if test "$allow_undefined_flag" = unsupported; then func_warning "undefined symbols not allowed in $host shared libraries" build_libtool_libs=no build_old_libs=yes fi else # Don't allow undefined symbols. allow_undefined_flag="$no_undefined_flag" fi fi func_generate_dlsyms "$libname" "$libname" "yes" func_append libobjs " $symfileobj" test "X$libobjs" = "X " && libobjs= if test "$opt_mode" != relink; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= tempremovelist=`$ECHO "$output_objdir/*"` for p in $tempremovelist; do case $p in *.$objext | *.gcno) ;; $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) if test "X$precious_files_regex" != "X"; then if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue fi fi func_append removelist " $p" ;; *) ;; esac done test -n "$removelist" && \ func_show_eval "${RM}r \$removelist" fi # Now set the variables for building old libraries. if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then func_append oldlibs " $output_objdir/$libname.$libext" # Transform .lo files to .o files. oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP` fi # Eliminate all temporary directories. #for path in $notinst_path; do # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` #done if test -n "$xrpath"; then # If the user specified any rpath flags, then add them. temp_xrpath= for libdir in $xrpath; do func_replace_sysroot "$libdir" func_append temp_xrpath " -R$func_replace_sysroot_result" case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened old_dlfiles="$dlfiles" dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in *" $lib "*) ;; *) func_append dlfiles " $lib" ;; esac done # Make sure dlprefiles contains only unique files old_dlprefiles="$dlprefiles" dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in *" $lib "*) ;; *) func_append dlprefiles " $lib" ;; esac done if test "$build_libtool_libs" = yes; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) # these systems don't actually have a c library (as such)! ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C library is in the System framework func_append deplibs " System.ltframework" ;; *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work ;; *) # Add libc to deplibs on all other systems if necessary. if test "$build_libtool_need_lc" = "yes"; then func_append deplibs " -lc" fi ;; esac fi # Transform deplibs into only deplibs that can be linked in shared. name_save=$name libname_save=$libname release_save=$release versuffix_save=$versuffix major_save=$major # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? release="" versuffix="" major="" newdeplibs= droppeddeps=no case $deplibs_check_method in pass_all) # Don't check for shared/static. Everything works. # This might be a little naive. We might want to check # whether the library exists or not. But this is on # osf3 & osf4 and I'm not really sure... Just # implementing what was already the behavior. newdeplibs=$deplibs ;; test_compile) # This code stresses the "libraries are programs" paradigm to its # limits. Maybe even breaks it. We compile a program, linking it # against the deplibs as a proxy for the library. Then we can check # whether they linked in statically or dynamically with ldd. $opt_dry_run || $RM conftest.c cat > conftest.c </dev/null` $nocaseglob else potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` fi for potent_lib in $potential_libs; do # Follow soft links. if ls -lLd "$potent_lib" 2>/dev/null | $GREP " -> " >/dev/null; then continue fi # The statement above tries to avoid entering an # endless loop below, in case of cyclic links. # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? potlib="$potent_lib" while test -h "$potlib" 2>/dev/null; do potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` case $potliblink in [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; *) potlib=`$ECHO "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | $SED -e 10q | $EGREP "$file_magic_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib="" break 2 fi done done fi if test -n "$a_deplib" ; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib" ; then $ECHO "*** with $libname but no candidates were found. (...for file magic test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a file magic. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` for a_deplib in $deplibs; do case $a_deplib in -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then case " $predeps $postdeps " in *" $a_deplib "*) func_append newdeplibs " $a_deplib" a_deplib="" ;; esac fi if test -n "$a_deplib" ; then libname=`eval "\\$ECHO \"$libname_spec\""` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do potlib="$potent_lib" # see symlink-check above in file_magic test if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ $EGREP "$match_pattern_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib="" break 2 fi done done fi if test -n "$a_deplib" ; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib" ; then $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a regex pattern. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; none | unknown | *) newdeplibs="" tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then for i in $predeps $postdeps ; do # can't use Xsed below, because $i might contain '/' tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s,$i,,"` done fi case $tmp_deplibs in *[!\ \ ]*) echo if test "X$deplibs_check_method" = "Xnone"; then echo "*** Warning: inter-library dependencies are not supported in this platform." else echo "*** Warning: inter-library dependencies are not known to be supported." fi echo "*** All declared inter-library dependencies are being dropped." droppeddeps=yes ;; esac ;; esac versuffix=$versuffix_save major=$major_save release=$release_save libname=$libname_save name=$name_save case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library with the System framework newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac if test "$droppeddeps" = yes; then if test "$module" = yes; then echo echo "*** Warning: libtool could not satisfy all declared inter-library" $ECHO "*** dependencies of module $libname. Therefore, libtool will create" echo "*** a static module, that should work as long as the dlopening" echo "*** application is linked with the -dlopen flag." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using \`nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** \`nm' from GNU binutils and a full rebuild may help." fi if test "$build_old_libs" = no; then oldlibs="$output_objdir/$libname.$libext" build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi else echo "*** The inter-library dependencies that have been dropped here will be" echo "*** automatically added whenever a program is linked with this library" echo "*** or is declared to -dlopen it." if test "$allow_undefined" = no; then echo echo "*** Since this library must not contain undefined symbols," echo "*** because either the platform does not support them or" echo "*** it was explicitly requested with -no-undefined," echo "*** libtool will only create a static version of it." if test "$build_old_libs" = no; then oldlibs="$output_objdir/$libname.$libext" build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi fi fi # Done checking deplibs! deplibs=$newdeplibs fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" case $host in *-*-darwin*) newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done deplibs="$new_libs" # All the library-specific variables (install_libdir is set above). library_names= old_library= dlname= # Test again, we may have decided not to build it any more if test "$build_libtool_libs" = yes; then # Remove ${wl} instances when linking with ld. # FIXME: should test the right _cmds variable. case $archive_cmds in *\$LD\ *) wl= ;; esac if test "$hardcode_into_libs" = yes; then # Hardcode the library paths hardcode_libdirs= dep_rpath= rpath="$finalize_rpath" test "$opt_mode" != relink && rpath="$compile_rpath$rpath" for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then func_replace_sysroot "$libdir" libdir=$func_replace_sysroot_result if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append dep_rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" fi test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi shlibpath="$finalize_shlibpath" test "$opt_mode" != relink && shlibpath="$compile_shlibpath$shlibpath" if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi # Get the real and link names of the library. eval shared_ext=\"$shrext_cmds\" eval library_names=\"$library_names_spec\" set dummy $library_names shift realname="$1" shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else soname="$realname" fi if test -z "$dlname"; then dlname=$soname fi lib="$output_objdir/$realname" linknames= for link do func_append linknames " $link" done # Use standard objects if they are pic test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` test "X$libobjs" = "X " && libobjs= delfiles= if test -n "$export_symbols" && test -n "$include_expsyms"; then $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" export_symbols="$output_objdir/$libname.uexp" func_append delfiles " $export_symbols" fi orig_export_symbols= case $host_os in cygwin* | mingw* | cegcc*) if test -n "$export_symbols" && test -z "$export_symbols_regex"; then # exporting using user supplied symfile if test "x`$SED 1q $export_symbols`" != xEXPORTS; then # and it's NOT already a .def file. Must figure out # which of the given symbols are data symbols and tag # them as such. So, trigger use of export_symbols_cmds. # export_symbols gets reassigned inside the "prepare # the list of exported symbols" if statement, so the # include_expsyms logic still works. orig_export_symbols="$export_symbols" export_symbols= always_export_symbols=yes fi fi ;; esac # Prepare the list of exported symbols if test -z "$export_symbols"; then if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then func_verbose "generating symbol list for \`$libname.la'" export_symbols="$output_objdir/$libname.exp" $opt_dry_run || $RM $export_symbols cmds=$export_symbols_cmds save_ifs="$IFS"; IFS='~' for cmd1 in $cmds; do IFS="$save_ifs" # Take the normal branch if the nm_file_list_spec branch # doesn't work or if tool conversion is not needed. case $nm_file_list_spec~$to_tool_file_cmd in *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) try_normal_branch=yes eval cmd=\"$cmd1\" func_len " $cmd" len=$func_len_result ;; *) try_normal_branch=no ;; esac if test "$try_normal_branch" = yes \ && { test "$len" -lt "$max_cmd_len" \ || test "$max_cmd_len" -le -1; } then func_show_eval "$cmd" 'exit $?' skipped_export=false elif test -n "$nm_file_list_spec"; then func_basename "$output" output_la=$func_basename_result save_libobjs=$libobjs save_output=$output output=${output_objdir}/${output_la}.nm func_to_tool_file "$output" libobjs=$nm_file_list_spec$func_to_tool_file_result func_append delfiles " $output" func_verbose "creating $NM input file list: $output" for obj in $save_libobjs; do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > "$output" eval cmd=\"$cmd1\" func_show_eval "$cmd" 'exit $?' output=$save_output libobjs=$save_libobjs skipped_export=false else # The command line is too long to execute in one step. func_verbose "using reloadable object file for export list..." skipped_export=: # Break out early, otherwise skipped_export may be # set to false by a later but shorter cmd. break fi done IFS="$save_ifs" if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi fi if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols="$export_symbols" test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi tmp_deplibs= for test_deplib in $deplibs; do case " $convenience " in *" $test_deplib "*) ;; *) func_append tmp_deplibs " $test_deplib" ;; esac done deplibs="$tmp_deplibs" if test -n "$convenience"; then if test -n "$whole_archive_flag_spec" && test "$compiler_needs_object" = yes && test -z "$libobjs"; then # extract the archives, so we have objects to list. # TODO: could optimize this to just extract one archive. whole_archive_flag_spec= fi if test -n "$whole_archive_flag_spec"; then save_libobjs=$libobjs eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= else gentop="$output_objdir/${outputname}x" func_append generated " $gentop" func_extract_archives $gentop $convenience func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi fi if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" func_append linker_flags " $flag" fi # Make a backup of the uninstalled library when relinking if test "$opt_mode" = relink; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi # Do each of the archive commands. if test "$module" = yes && test -n "$module_cmds" ; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds else eval test_cmds=\"$module_cmds\" cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then eval test_cmds=\"$archive_expsym_cmds\" cmds=$archive_expsym_cmds else eval test_cmds=\"$archive_cmds\" cmds=$archive_cmds fi fi if test "X$skipped_export" != "X:" && func_len " $test_cmds" && len=$func_len_result && test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then : else # The command line is too long to link in one step, link piecewise # or, if using GNU ld and skipped_export is not :, use a linker # script. # Save the value of $output and $libobjs because we want to # use them later. If we have whole_archive_flag_spec, we # want to use save_libobjs as it was before # whole_archive_flag_spec was expanded, because we can't # assume the linker understands whole_archive_flag_spec. # This may have to be revisited, in case too many # convenience libraries get linked in and end up exceeding # the spec. if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then save_libobjs=$libobjs fi save_output=$output func_basename "$output" output_la=$func_basename_result # Clear the reloadable object creation command queue and # initialize k to one. test_cmds= concat_cmds= objlist= last_robj= k=1 if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then output=${output_objdir}/${output_la}.lnkscript func_verbose "creating GNU ld script: $output" echo 'INPUT (' > $output for obj in $save_libobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done echo ')' >> $output func_append delfiles " $output" func_to_tool_file "$output" output=$func_to_tool_file_result elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then output=${output_objdir}/${output_la}.lnk func_verbose "creating linker input file list: $output" : > $output set x $save_libobjs shift firstobj= if test "$compiler_needs_object" = yes; then firstobj="$1 " shift fi for obj do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done func_append delfiles " $output" func_to_tool_file "$output" output=$firstobj\"$file_list_spec$func_to_tool_file_result\" else if test -n "$save_libobjs"; then func_verbose "creating reloadable object files..." output=$output_objdir/$output_la-${k}.$objext eval test_cmds=\"$reload_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 # Loop over the list of objects to be linked. for obj in $save_libobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result if test "X$objlist" = X || test "$len" -lt "$max_cmd_len"; then func_append objlist " $obj" else # The command $test_cmds is almost too long, add a # command to the queue. if test "$k" -eq 1 ; then # The first file doesn't have a previous command to add. reload_objs=$objlist eval concat_cmds=\"$reload_cmds\" else # All subsequent reloadable object files will link in # the last one created. reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" fi last_robj=$output_objdir/$output_la-${k}.$objext func_arith $k + 1 k=$func_arith_result output=$output_objdir/$output_la-${k}.$objext objlist=" $obj" func_len " $last_robj" func_arith $len0 + $func_len_result len=$func_arith_result fi done # Handle the remaining objects by creating one last # reloadable object file. All subsequent reloadable object # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ reload_objs="$objlist $last_robj" eval concat_cmds=\"\${concat_cmds}$reload_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" fi func_append delfiles " $output" else output= fi if ${skipped_export-false}; then func_verbose "generating symbol list for \`$libname.la'" export_symbols="$output_objdir/$libname.exp" $opt_dry_run || $RM $export_symbols libobjs=$output # Append the command to create the export file. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi fi test -n "$save_libobjs" && func_verbose "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. save_ifs="$IFS"; IFS='~' for cmd in $concat_cmds; do IFS="$save_ifs" $opt_silent || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test "$opt_mode" = relink; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS="$save_ifs" if test -n "$export_symbols_regex" && ${skipped_export-false}; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi if ${skipped_export-false}; then if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols="$export_symbols" test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi fi libobjs=$output # Restore the value of output. output=$save_output if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= fi # Expand the library linking commands again to reset the # value of $libobjs for piecewise linking. # Do each of the archive commands. if test "$module" = yes && test -n "$module_cmds" ; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then cmds=$archive_expsym_cmds else cmds=$archive_cmds fi fi fi if test -n "$delfiles"; then # Append the command to remove temporary files to $cmds. eval cmds=\"\$cmds~\$RM $delfiles\" fi # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop="$output_objdir/${outputname}x" func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" $opt_silent || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test "$opt_mode" = relink; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS="$save_ifs" # Restore the uninstalled library and exit if test "$opt_mode" = relink; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? if test -n "$convenience"; then if test -z "$whole_archive_flag_spec"; then func_show_eval '${RM}r "$gentop"' fi fi exit $EXIT_SUCCESS fi # Create links to the real library. for linkname in $linknames; do if test "$realname" != "$linkname"; then func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' fi done # If -module or -export-dynamic was specified, set the dlname. if test "$module" = yes || test "$export_dynamic" = yes; then # On all known operating systems, these are identical. dlname="$soname" fi fi ;; obj) if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then func_warning "\`-dlopen' is ignored for objects" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "\`-l' and \`-L' are ignored for objects" ;; esac test -n "$rpath" && \ func_warning "\`-rpath' is ignored for objects" test -n "$xrpath" && \ func_warning "\`-R' is ignored for objects" test -n "$vinfo" && \ func_warning "\`-version-info' is ignored for objects" test -n "$release" && \ func_warning "\`-release' is ignored for objects" case $output in *.lo) test -n "$objs$old_deplibs" && \ func_fatal_error "cannot build library object \`$output' from non-libtool objects" libobj=$output func_lo2o "$libobj" obj=$func_lo2o_result ;; *) libobj= obj="$output" ;; esac # Delete the old objects. $opt_dry_run || $RM $obj $libobj # Objects from convenience libraries. This assumes # single-version convenience libraries. Whenever we create # different ones for PIC/non-PIC, this we'll have to duplicate # the extraction. reload_conv_objs= gentop= # reload_cmds runs $LD directly, so let us get rid of # -Wl from whole_archive_flag_spec and hope we can get by with # turning comma into space.. wl= if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` else gentop="$output_objdir/${obj}x" func_append generated " $gentop" func_extract_archives $gentop $convenience reload_conv_objs="$reload_objs $func_extract_archives_result" fi fi # If we're not building shared, we need to use non_pic_objs test "$build_libtool_libs" != yes && libobjs="$non_pic_objects" # Create the old-style object. reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test output="$obj" func_execute_cmds "$reload_cmds" 'exit $?' # Exit if we aren't doing a library object file. if test -z "$libobj"; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS fi if test "$build_libtool_libs" != yes; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi # Create an invalid libtool object if no PIC, so that we don't # accidentally link it into a program. # $show "echo timestamp > $libobj" # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS fi if test -n "$pic_flag" || test "$pic_mode" != default; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" output="$libobj" func_execute_cmds "$reload_cmds" 'exit $?' fi if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS ;; prog) case $host in *cygwin*) func_stripname '' '.exe' "$output" output=$func_stripname_result.exe;; esac test -n "$vinfo" && \ func_warning "\`-version-info' is ignored for programs" test -n "$release" && \ func_warning "\`-release' is ignored for programs" test "$preload" = yes \ && test "$dlopen_support" = unknown \ && test "$dlopen_self" = unknown \ && test "$dlopen_self_static" = unknown && \ func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac case $host in *-*-darwin*) # Don't allow lazy linking, it breaks C++ global constructors # But is supposedly fixed on 10.4 or later (yay!). if test "$tagname" = CXX ; then case ${MACOSX_DEPLOYMENT_TARGET-10.0} in 10.[0123]) func_append compile_command " ${wl}-bind_at_load" func_append finalize_command " ${wl}-bind_at_load" ;; esac fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $compile_deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $compile_deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done compile_deplibs="$new_libs" func_append compile_command " $compile_deplibs" func_append finalize_command " $finalize_deplibs" if test -n "$rpath$xrpath"; then # If the user specified any rpath flags, then add them. for libdir in $rpath $xrpath; do # This is the magic to use -rpath. case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done fi # Now hardcode the library paths rpath= hardcode_libdirs= for libdir in $compile_rpath $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; ::) dllsearchpath=$libdir;; *) func_append dllsearchpath ":$libdir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" eval rpath=\" $hardcode_libdir_flag_spec\" fi compile_rpath="$rpath" rpath= hardcode_libdirs= for libdir in $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$finalize_perm_rpath " in *" $libdir "*) ;; *) func_append finalize_perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" eval rpath=\" $hardcode_libdir_flag_spec\" fi finalize_rpath="$rpath" if test -n "$libobjs" && test "$build_old_libs" = yes; then # Transform all the library objects into standard objects. compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` fi func_generate_dlsyms "$outputname" "@PROGRAM@" "no" # template prelinking step if test -n "$prelink_cmds"; then func_execute_cmds "$prelink_cmds" 'exit $?' fi wrappers_required=yes case $host in *cegcc* | *mingw32ce*) # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. wrappers_required=no ;; *cygwin* | *mingw* ) if test "$build_libtool_libs" != yes; then wrappers_required=no fi ;; *) if test "$need_relink" = no || test "$build_libtool_libs" != yes; then wrappers_required=no fi ;; esac if test "$wrappers_required" = no; then # Replace the output file specification. compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` link_command="$compile_command$compile_rpath" # We have no uninstalled library dependencies, so finalize right now. exit_status=0 func_show_eval "$link_command" 'exit_status=$?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Delete the generated files. if test -f "$output_objdir/${outputname}S.${objext}"; then func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' fi exit $exit_status fi if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" fi if test -n "$finalize_shlibpath"; then finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" fi compile_var= finalize_var= if test -n "$runpath_var"; then if test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi if test -n "$finalize_perm_rpath"; then # We should set the runpath_var. rpath= for dir in $finalize_perm_rpath; do func_append rpath "$dir:" done finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi fi if test "$no_install" = yes; then # We don't need to create a wrapper script. link_command="$compile_var$compile_command$compile_rpath" # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. $opt_dry_run || $RM $output # Link the executable and exit func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi exit $EXIT_SUCCESS fi if test "$hardcode_action" = relink; then # Fast installation is not supported link_command="$compile_var$compile_command$compile_rpath" relink_command="$finalize_var$finalize_command$finalize_rpath" func_warning "this platform does not like uninstalled shared libraries" func_warning "\`$output' will be relinked during installation" else if test "$fast_install" != no; then link_command="$finalize_var$compile_command$finalize_rpath" if test "$fast_install" = yes; then relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` else # fast_install is set to needless relink_command= fi else link_command="$compile_var$compile_command$compile_rpath" relink_command="$finalize_var$finalize_command$finalize_rpath" fi fi # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` # Delete the old output files. $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output_objdir/$outputname" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Now create the wrapper script. func_verbose "creating $output" # Quote the relink command for shipping. if test -n "$relink_command"; then # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done relink_command="(cd `pwd`; $relink_command)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` fi # Only actually do things if not in dry run mode. $opt_dry_run || { # win32 will think the script is a binary if it has # a .exe suffix, so we strip it off here. case $output in *.exe) func_stripname '' '.exe' "$output" output=$func_stripname_result ;; esac # test for cygwin because mv fails w/o .exe extensions case $host in *cygwin*) exeext=.exe func_stripname '' '.exe' "$outputname" outputname=$func_stripname_result ;; *) exeext= ;; esac case $host in *cygwin* | *mingw* ) func_dirname_and_basename "$output" "" "." output_name=$func_basename_result output_path=$func_dirname_result cwrappersource="$output_path/$objdir/lt-$output_name.c" cwrapper="$output_path/$output_name.exe" $RM $cwrappersource $cwrapper trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 func_emit_cwrapperexe_src > $cwrappersource # The wrapper executable is built using the $host compiler, # because it contains $host paths and files. If cross- # compiling, it, like the target executable, must be # executed on the $host or under an emulation environment. $opt_dry_run || { $LTCC $LTCFLAGS -o $cwrapper $cwrappersource $STRIP $cwrapper } # Now, create the wrapper script for func_source use: func_ltwrapper_scriptname $cwrapper $RM $func_ltwrapper_scriptname_result trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 $opt_dry_run || { # note: this script will not be executed, so do not chmod. if test "x$build" = "x$host" ; then $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result else func_emit_wrapper no > $func_ltwrapper_scriptname_result fi } ;; * ) $RM $output trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 func_emit_wrapper no > $output chmod +x $output ;; esac } exit $EXIT_SUCCESS ;; esac # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do if test "$build_libtool_libs" = convenience; then oldobjs="$libobjs_save $symfileobj" addlibs="$convenience" build_libtool_libs=no else if test "$build_libtool_libs" = module; then oldobjs="$libobjs_save" build_libtool_libs=no else oldobjs="$old_deplibs $non_pic_objects" if test "$preload" = yes && test -f "$symfileobj"; then func_append oldobjs " $symfileobj" fi fi addlibs="$old_convenience" fi if test -n "$addlibs"; then gentop="$output_objdir/${outputname}x" func_append generated " $gentop" func_extract_archives $gentop $addlibs func_append oldobjs " $func_extract_archives_result" fi # Do each command in the archive commands. if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then cmds=$old_archive_from_new_cmds else # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop="$output_objdir/${outputname}x" func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append oldobjs " $func_extract_archives_result" fi # POSIX demands no paths to be encoded in archives. We have # to avoid creating archives with duplicate basenames if we # might have to extract them afterwards, e.g., when creating a # static archive out of a convenience library, or when linking # the entirety of a libtool archive into another (currently # not supported by libtool). if (for obj in $oldobjs do func_basename "$obj" $ECHO "$func_basename_result" done | sort | sort -uc >/dev/null 2>&1); then : else echo "copying selected object files to avoid basename conflicts..." gentop="$output_objdir/${outputname}x" func_append generated " $gentop" func_mkdir_p "$gentop" save_oldobjs=$oldobjs oldobjs= counter=1 for obj in $save_oldobjs do func_basename "$obj" objbase="$func_basename_result" case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) while :; do # Make sure we don't pick an alternate name that also # overlaps. newobj=lt$counter-$objbase func_arith $counter + 1 counter=$func_arith_result case " $oldobjs " in *[\ /]"$newobj "*) ;; *) if test ! -f "$gentop/$newobj"; then break; fi ;; esac done func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" func_append oldobjs " $gentop/$newobj" ;; *) func_append oldobjs " $obj" ;; esac done fi func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result eval cmds=\"$old_archive_cmds\" func_len " $cmds" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then cmds=$old_archive_cmds elif test -n "$archiver_list_spec"; then func_verbose "using command file archive linking..." for obj in $oldobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > $output_objdir/$libname.libcmd func_to_tool_file "$output_objdir/$libname.libcmd" oldobjs=" $archiver_list_spec$func_to_tool_file_result" cmds=$old_archive_cmds else # the command line is too long to link in one step, link in parts func_verbose "using piecewise archive linking..." save_RANLIB=$RANLIB RANLIB=: objlist= concat_cmds= save_oldobjs=$oldobjs oldobjs= # Is there a better way of finding the last object in the list? for obj in $save_oldobjs do last_oldobj=$obj done eval test_cmds=\"$old_archive_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 for obj in $save_oldobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result func_append objlist " $obj" if test "$len" -lt "$max_cmd_len"; then : else # the above command should be used before it gets too long oldobjs=$objlist if test "$obj" = "$last_oldobj" ; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" objlist= len=$len0 fi done RANLIB=$save_RANLIB oldobjs=$objlist if test "X$oldobjs" = "X" ; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" fi fi fi func_execute_cmds "$cmds" 'exit $?' done test -n "$generated" && \ func_show_eval "${RM}r$generated" # Now create the libtool archive. case $output in *.la) old_library= test "$build_old_libs" = yes && old_library="$libname.$libext" func_verbose "creating $output" # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done # Quote the link command for shipping. relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` if test "$hardcode_automatic" = yes ; then relink_command= fi # Only create the output if not a dry run. $opt_dry_run || { for installed in no yes; do if test "$installed" = yes; then if test -z "$install_libdir"; then break fi output="$output_objdir/$outputname"i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) func_basename "$deplib" name="$func_basename_result" func_resolve_sysroot "$deplib" eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` test -z "$libdir" && \ func_fatal_error "\`$deplib' is not a valid libtool archive" func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" ;; -L*) func_stripname -L '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -L$func_replace_sysroot_result" ;; -R*) func_stripname -R '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -R$func_replace_sysroot_result" ;; *) func_append newdependency_libs " $deplib" ;; esac done dependency_libs="$newdependency_libs" newdlfiles= for lib in $dlfiles; do case $lib in *.la) func_basename "$lib" name="$func_basename_result" eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "\`$lib' is not a valid libtool archive" func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" ;; *) func_append newdlfiles " $lib" ;; esac done dlfiles="$newdlfiles" newdlprefiles= for lib in $dlprefiles; do case $lib in *.la) # Only pass preopened files to the pseudo-archive (for # eventual linking with the app. that links it) if we # didn't already link the preopened objects directly into # the library: func_basename "$lib" name="$func_basename_result" eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "\`$lib' is not a valid libtool archive" func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" ;; esac done dlprefiles="$newdlprefiles" else newdlfiles= for lib in $dlfiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlfiles " $abs" done dlfiles="$newdlfiles" newdlprefiles= for lib in $dlprefiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlprefiles " $abs" done dlprefiles="$newdlprefiles" fi $RM $output # place dlname in correct position for cygwin # In fact, it would be nice if we could use this code for all target # systems that can't hard-code library paths into their executables # and that have no shared library path variable independent of PATH, # but it turns out we can't easily determine that from inspecting # libtool variables, so we have to hard-code the OSs to which it # applies here; at the moment, that means platforms that use the PE # object format with DLL files. See the long comment at the top of # tests/bindir.at for full details. tdlname=$dlname case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) # If a -bindir argument was supplied, place the dll there. if test "x$bindir" != x ; then func_relative_path "$install_libdir" "$bindir" tdlname=$func_relative_path_result$dlname else # Otherwise fall back on heuristic. tdlname=../bin/$dlname fi ;; esac $ECHO > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='$tdlname' # Names of this library. library_names='$library_names' # The name of the static archive. old_library='$old_library' # Linker flags that can not go in dependency_libs. inherited_linker_flags='$new_inherited_linker_flags' # Libraries that this one depends upon. dependency_libs='$dependency_libs' # Names of additional weak libraries provided by this library weak_library_names='$weak_libs' # Version information for $libname. current=$current age=$age revision=$revision # Is this an already installed library? installed=$installed # Should we warn about portability when linking against -modules? shouldnotlink=$module # Files to dlopen/dlpreopen dlopen='$dlfiles' dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" if test "$installed" = no && test "$need_relink" = yes; then $ECHO >> $output "\ relink_command=\"$relink_command\"" fi done } # Do a symbolic link so that the libtool archive can be found in # LD_LIBRARY_PATH before the program is installed. func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' ;; esac exit $EXIT_SUCCESS } { test "$opt_mode" = link || test "$opt_mode" = relink; } && func_mode_link ${1+"$@"} # func_mode_uninstall arg... func_mode_uninstall () { $opt_debug RM="$nonopt" files= rmforce= exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic="$magic" for arg do case $arg in -f) func_append RM " $arg"; rmforce=yes ;; -*) func_append RM " $arg" ;; *) func_append files " $arg" ;; esac done test -z "$RM" && \ func_fatal_help "you must specify an RM program" rmdirs= for file in $files; do func_dirname "$file" "" "." dir="$func_dirname_result" if test "X$dir" = X.; then odir="$objdir" else odir="$dir/$objdir" fi func_basename "$file" name="$func_basename_result" test "$opt_mode" = uninstall && odir="$dir" # Remember odir for removal later, being careful to avoid duplicates if test "$opt_mode" = clean; then case " $rmdirs " in *" $odir "*) ;; *) func_append rmdirs " $odir" ;; esac fi # Don't error if the file doesn't exist and rm -f was used. if { test -L "$file"; } >/dev/null 2>&1 || { test -h "$file"; } >/dev/null 2>&1 || test -f "$file"; then : elif test -d "$file"; then exit_status=1 continue elif test "$rmforce" = yes; then continue fi rmfiles="$file" case $name in *.la) # Possibly a libtool archive, so verify it. if func_lalib_p "$file"; then func_source $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do func_append rmfiles " $odir/$n" done test -n "$old_library" && func_append rmfiles " $odir/$old_library" case "$opt_mode" in clean) case " $library_names " in *" $dlname "*) ;; *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; esac test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; esac fi ;; *.lo) # Possibly a libtool object, so verify it. if func_lalib_p "$file"; then # Read the .lo file func_source $dir/$name # Add PIC object to the list of files to remove. if test -n "$pic_object" && test "$pic_object" != none; then func_append rmfiles " $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. if test -n "$non_pic_object" && test "$non_pic_object" != none; then func_append rmfiles " $dir/$non_pic_object" fi fi ;; *) if test "$opt_mode" = clean ; then noexename=$name case $file in *.exe) func_stripname '' '.exe' "$file" file=$func_stripname_result func_stripname '' '.exe' "$name" noexename=$func_stripname_result # $file with .exe has already been added to rmfiles, # add $file without .exe func_append rmfiles " $file" ;; esac # Do a test to see if this is a libtool program. if func_ltwrapper_p "$file"; then if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" relink_command= func_source $func_ltwrapper_scriptname_result func_append rmfiles " $func_ltwrapper_scriptname_result" else relink_command= func_source $dir/$noexename fi # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles func_append rmfiles " $odir/$name $odir/${name}S.${objext}" if test "$fast_install" = yes && test -n "$relink_command"; then func_append rmfiles " $odir/lt-$name" fi if test "X$noexename" != "X$name" ; then func_append rmfiles " $odir/lt-${noexename}.c" fi fi fi ;; esac func_show_eval "$RM $rmfiles" 'exit_status=1' done # Try to remove the ${objdir}s in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then func_show_eval "rmdir $dir >/dev/null 2>&1" fi done exit $exit_status } { test "$opt_mode" = uninstall || test "$opt_mode" = clean; } && func_mode_uninstall ${1+"$@"} test -z "$opt_mode" && { help="$generic_help" func_fatal_help "you must specify a MODE" } test -z "$exec_cmd" && \ func_fatal_help "invalid operation mode \`$opt_mode'" if test -n "$exec_cmd"; then eval exec "$exec_cmd" exit $EXIT_FAILURE fi exit $exit_status # The TAGs below are defined such that we never get into a situation # in which we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support # them. This is particularly important on AIX, because we don't # support having both static and shared libraries enabled at the same # time on that platform, so we default to a shared-only configuration. # If a disable-shared tag is given, we'll fallback to a static-only # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared build_libtool_libs=no build_old_libs=yes # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: # vi:sw=2 tesseract-3.04.01/config/missing000077500000000000000000000153311266071204500165260ustar00rootroot00000000000000#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2012-06-26.16; # UTC # Copyright (C) 1996-2013 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=http://www.perl.org/ flex_URL=http://flex.sourceforge.net/ gnu_software_URL=http://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'automa4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: tesseract-3.04.01/configure000077500000000000000000022525621266071204500156040ustar00rootroot00000000000000#! /bin/sh # From configure.ac {gitrev}. # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for tesseract 3.04.01. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1 test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: https://github.com/tesseract-ocr/tesseract/issues about $0: your system, including any error possibly output before $0: this message. Then install a modern shell, or manually $0: run the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" SHELL=${CONFIG_SHELL-/bin/sh} test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='tesseract' PACKAGE_TARNAME='tesseract' PACKAGE_VERSION='3.04.01' PACKAGE_STRING='tesseract 3.04.01' PACKAGE_BUGREPORT='https://github.com/tesseract-ocr/tesseract/issues' PACKAGE_URL='' ac_unique_file="api/tesseractmain.cpp" ac_default_prefix=/usr/local # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS ENABLE_TRAINING_FALSE ENABLE_TRAINING_TRUE LIBLEPT_HEADERSDIR OTOOL64 OTOOL LIPO NMEDIT DSYMUTIL MANIFEST_TOOL RANLIB ac_ct_AR AR DLLTOOL OBJDUMP LN_S NM ac_ct_DUMPBIN DUMPBIN LD FGREP SED am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE ac_ct_CC CFLAGS CC LIBTOOL NO_TESSDATA_PREFIX_FALSE NO_TESSDATA_PREFIX_TRUE USING_MULTIPLELIBS_FALSE USING_MULTIPLELIBS_TRUE VISIBILITY_FALSE VISIBILITY_TRUE OPENCL_LDFLAGS OPENCL_CPPFLAGS USE_OPENCL_FALSE USE_OPENCL_TRUE FRAMEWORK_OPENCL EGREP GREP CXXCPP OPENMP_CXXFLAGS OPENMP_FALSE OPENMP_TRUE EMBEDDED_FALSE EMBEDDED_TRUE AM_CPPFLAGS NO_CUBE_BUILD_FALSE NO_CUBE_BUILD_TRUE AM_LDFLAGS ADD_RT_FALSE ADD_RT_TRUE host_os host_vendor host_cpu host build_os build_vendor build_cpu build GRAPHICS_DISABLED_FALSE GRAPHICS_DISABLED_TRUE OSX_FALSE OSX_TRUE MINGW_FALSE MINGW_TRUE T_WIN_FALSE T_WIN_TRUE MAINT MAINTAINER_MODE_FALSE MAINTAINER_MODE_TRUE AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V am__fastdepCXX_FALSE am__fastdepCXX_TRUE CXXDEPMODE am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__quote am__include DEPDIR am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM GENERIC_VERSION GENERIC_RELEASE GENERIC_LIBRARY_NAME GENERIC_LIBRARY_VERSION GENERIC_MAJOR_VERSION GENERIC_API_VERSION PACKAGE_DATE PACKAGE_YEAR OBJEXT EXEEXT ac_ct_CXX CPPFLAGS LDFLAGS CXXFLAGS CXX target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_dependency_tracking enable_silent_rules enable_maintainer_mode with_extra_includes with_extra_libraries enable_graphics enable_cube enable_embedded enable_openmp enable_opencl enable_visibility enable_multiple_libraries enable_tessdata_prefix enable_debug enable_shared enable_static with_pic enable_fast_install with_gnu_ld with_sysroot enable_libtool_lock enable_largefile ' ac_precious_vars='build_alias host_alias target_alias CXX CXXFLAGS LDFLAGS LIBS CPPFLAGS CCC CXXCPP CC CFLAGS LIBLEPT_HEADERSDIR' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures tesseract 3.04.01 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/tesseract] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of tesseract 3.04.01:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") --enable-maintainer-mode enable make rules and dependencies not useful (and sometimes confusing) to the casual installer --enable-graphics enable graphics (ScrollView) (default) --disable-graphics disable graphics (ScrollView) --disable-cube don't build cube support (experimental) --enable-embedded enable embedded build (default=no) --disable-openmp do not use OpenMP --enable-opencl enable opencl build (default=no) --enable-visibility enable experimental build with fvisibility (default=no) --enable-multiple-libraries enable multiple libraries (default=no) --disable-tessdata-prefix don't set TESSDATA-PREFIX during compile --enable-debug turn on debugging (default=no) --enable-shared[=PKGS] build shared libraries [default=yes] --enable-static[=PKGS] build static libraries [default=yes] --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --disable-largefile omit support for large files Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-extra-includes=DIR Define an additional directory for include files --with-extra-libraries=DIR Define an additional directory for library files --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use both] --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-sysroot=DIR Search for dependent libraries within DIR (or the compiler's sysroot if not specified). Some influential environment variables: CXX C++ compiler command CXXFLAGS C++ compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CXXCPP C++ preprocessor CC C compiler command CFLAGS C compiler flags LIBLEPT_HEADERSDIR Leptonica headers directory Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF tesseract configure 3.04.01 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_cxx_try_compile LINENO # ---------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile # ac_fn_cxx_try_link LINENO # ------------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_link # ac_fn_cxx_try_cpp LINENO # ------------------------ # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_cpp # ac_fn_cxx_check_header_mongrel LINENO HEADER VAR INCLUDES # --------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_cxx_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## ---------------------------------------------------------------- ## ## Report this to https://github.com/tesseract-ocr/tesseract/issues ## ## ---------------------------------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_cxx_check_header_mongrel # ac_fn_cxx_try_run LINENO # ------------------------ # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_cxx_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_run # ac_fn_cxx_check_header_compile LINENO HEADER VAR INCLUDES # --------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_cxx_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_cxx_check_header_compile # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_cxx_check_func LINENO FUNC VAR # ------------------------------------ # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_cxx_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_cxx_check_func # ac_fn_cxx_check_type LINENO TYPE VAR INCLUDES # --------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache # variable VAR accordingly. ac_fn_cxx_check_type () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { if (sizeof ($2)) return 0; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { if (sizeof (($2))) return 0; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else eval "$3=yes" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_cxx_check_type cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by tesseract $as_me 3.04.01, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ clang++ do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 $as_echo "$CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ clang++ do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 $as_echo "$ac_ct_CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5 $as_echo_n "checking whether the C++ compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C++ compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5 $as_echo_n "checking for C++ compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C++ compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } if ${ac_cv_cxx_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 $as_echo "$ac_cv_cxx_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } if ${ac_cv_prog_cxx_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 $as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu CXXFLAGS=${CXXFLAGS:-""} ac_aux_dir= for ac_dir in config "$srcdir"/config; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in config \"$srcdir\"/config" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Define date of package, etc. Could be useful in auto-generated # documentation. PACKAGE_YEAR=2016 PACKAGE_DATE="02/12" abs_top_srcdir=`$as_dirname -- $0 || $as_expr X$0 : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X$0 : 'X\(//\)[^/]' \| \ X$0 : 'X\(//\)$' \| \ X$0 : 'X\(/\)' \| . 2>/dev/null || $as_echo X$0 | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` gitrev="`git --git-dir=${abs_top_srcdir}/.git --work-tree=${abs_top_srcdir} describe --always --tags`" if test -n "${gitrev}" ; then cat >>confdefs.h <<_ACEOF #define GIT_REV "${gitrev}" _ACEOF echo "Using git revision: ${gitrev}" fi cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "${PACKAGE_NAME}" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "${PACKAGE_VERSION}" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_YEAR "$PACKAGE_YEAR" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_DATE "$PACKAGE_DATE" _ACEOF GENERIC_LIBRARY_NAME=tesseract # Release versioning GENERIC_MAJOR_VERSION=3 GENERIC_MINOR_VERSION=4 GENERIC_MICRO_VERSION=1 # API version (often = GENERIC_MAJOR_VERSION.GENERIC_MINOR_VERSION) GENERIC_API_VERSION=$GENERIC_MAJOR_VERSION.$GENERIC_MINOR_VERSION GENERIC_LIBRARY_VERSION=$GENERIC_MAJOR_VERSION:$GENERIC_MINOR_VERSION PACKAGE=$GENERIC_LIBRARY_NAME GENERIC_VERSION=$GENERIC_MAJOR_VERSION.$GENERIC_MINOR_VERSION.$GENERIC_MICRO_VERSION GENERIC_RELEASE=$GENERIC_MAJOR_VERSION.$GENERIC_MINOR_VERSION # ---------------------------------------- # Automake configuration # ---------------------------------------- # Do not require README file (we use README.md) am__api_version='1.13' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 $as_echo_n "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi if test "$2" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi rm -f conftest.file test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` # expand $ac_aux_dir to an absolute path am_aux_dir=`cd $ac_aux_dir && pwd` if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if ${ac_cv_path_mkdir+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 $as_echo_n "checking for style of include used by $am_make... " >&6; } am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 $as_echo "$_am_result" >&6; } rm -f confinc confmf # Check whether --enable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then : enableval=$enable_dependency_tracking; fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 $as_echo_n "checking whether $am_make supports nested variables... " >&6; } if ${am_cv_make_support_nested_variables+:} false; then : $as_echo_n "(cached) " >&6 else if $as_echo 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 $as_echo "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='tesseract' VERSION='3.04.01' cat >>confdefs.h <<_ACEOF #define PACKAGE "$PACKAGE" _ACEOF cat >>confdefs.h <<_ACEOF #define VERSION "$VERSION" _ACEOF # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # mkdir_p='$(MKDIR_P)' # We need awk for the "check" target. The system "awk" is bad on # some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar pax cpio none' am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' depcc="$CXX" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CXX_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CXX_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CXX_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CXX_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 $as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then am__fastdepCXX_TRUE= am__fastdepCXX_FALSE='#' else am__fastdepCXX_TRUE='#' am__fastdepCXX_FALSE= fi ac_config_headers="$ac_config_headers config_auto.h:config/config.h.in" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 $as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } # Check whether --enable-maintainer-mode was given. if test "${enable_maintainer_mode+set}" = set; then : enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval else USE_MAINTAINER_MODE=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 $as_echo "$USE_MAINTAINER_MODE" >&6; } if test $USE_MAINTAINER_MODE = yes; then MAINTAINER_MODE_TRUE= MAINTAINER_MODE_FALSE='#' else MAINTAINER_MODE_TRUE='#' MAINTAINER_MODE_FALSE= fi MAINT=$MAINTAINER_MODE_TRUE # default conditional if false; then T_WIN_TRUE= T_WIN_FALSE='#' else T_WIN_TRUE='#' T_WIN_FALSE= fi if false; then MINGW_TRUE= MINGW_FALSE='#' else MINGW_TRUE='#' MINGW_FALSE= fi if false; then OSX_TRUE= OSX_FALSE='#' else OSX_TRUE='#' OSX_FALSE= fi if false; then GRAPHICS_DISABLED_TRUE= GRAPHICS_DISABLED_FALSE='#' else GRAPHICS_DISABLED_TRUE='#' GRAPHICS_DISABLED_FALSE= fi OPENCL_INC="/opt/AMDAPP/include" OPENCL_LIBS="-lOpenCL" ############################# # # Platform specific setup # ############################# # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac case "${host_os}" in mingw32*) cat >>confdefs.h <<_ACEOF #define MINGW 1 _ACEOF if true; then T_WIN_TRUE= T_WIN_FALSE='#' else T_WIN_TRUE='#' T_WIN_FALSE= fi if true; then MINGW_TRUE= MINGW_FALSE='#' else MINGW_TRUE='#' MINGW_FALSE= fi if false; then ADD_RT_TRUE= ADD_RT_FALSE='#' else ADD_RT_TRUE='#' ADD_RT_FALSE= fi AM_LDFLAGS='-Wl,-no-undefined -Wl,--as-needed' ;; cygwin*) if false; then ADD_RT_TRUE= ADD_RT_FALSE='#' else ADD_RT_TRUE='#' ADD_RT_FALSE= fi if true; then T_WIN_TRUE= T_WIN_FALSE='#' else T_WIN_TRUE='#' T_WIN_FALSE= fi AM_LDFLAGS='-Wl,-no-undefined -Wl,--as-needed' ;; solaris*) LIBS="-lsocket -lnsl -lrt -lxnet" if true; then ADD_RT_TRUE= ADD_RT_FALSE='#' else ADD_RT_TRUE='#' ADD_RT_FALSE= fi ;; *darwin*) OPENCL_LIBS="" OPENCL_INC="" if false; then ADD_RT_TRUE= ADD_RT_FALSE='#' else ADD_RT_TRUE='#' ADD_RT_FALSE= fi ;; powerpc-*-darwin*) OPENCL_LIBS="" ;; *) # default if true; then ADD_RT_TRUE= ADD_RT_FALSE='#' else ADD_RT_TRUE='#' ADD_RT_FALSE= fi ;; esac includedir="${includedir}/tesseract" # Check whether --with-extra-includes was given. if test "${with_extra_includes+set}" = set; then : withval=$with_extra_includes; if test -d "$withval" ; then CFLAGS="$CFLAGS -I$withval" else as_fn_error $? "Cannot stat directory $withval" "$LINENO" 5 fi fi # Check whether --with-extra-libraries was given. if test "${with_extra_libraries+set}" = set; then : withval=$with_extra_libraries; if test -d "$withval" ; then LDFLAGS="$LDFLAGS -L$withval" else as_fn_error $? "Cannot stat directory $withval" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-graphics argument" >&5 $as_echo_n "checking --enable-graphics argument... " >&6; } # Check whether --enable-graphics was given. if test "${enable_graphics+set}" = set; then : enableval=$enable_graphics; enable_graphics=$enableval else enable_graphics="yes" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_graphics" >&5 $as_echo "$enable_graphics" >&6; } if test "$enable_graphics" = "no"; then $as_echo "#define GRAPHICS_DISABLED /**/" >>confdefs.h if true; then GRAPHICS_DISABLED_TRUE= GRAPHICS_DISABLED_FALSE='#' else GRAPHICS_DISABLED_TRUE='#' GRAPHICS_DISABLED_FALSE= fi fi # Check if cube should be disabled { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to disable cube" >&5 $as_echo_n "checking whether to disable cube... " >&6; } # Check whether --enable-cube was given. if test "${enable_cube+set}" = set; then : enableval=$enable_cube; disable_cube="yes" else disable_cube="no" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $disable_cube" >&5 $as_echo "$disable_cube" >&6; } if test "$disable_cube" = "yes"; then NO_CUBE_BUILD_TRUE= NO_CUBE_BUILD_FALSE='#' else NO_CUBE_BUILD_TRUE='#' NO_CUBE_BUILD_FALSE= fi if test "$disable_cube" = "yes"; then AM_CPPFLAGS=-DNO_CUBE_BUILD fi # check whether to build embedded version { $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-embedded argument" >&5 $as_echo_n "checking --enable-embedded argument... " >&6; } # Check whether --enable-embedded was given. if test "${enable_embedded+set}" = set; then : enableval=$enable_embedded; enable_embedded=$enableval else enable_embedded="no" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_embedded" >&5 $as_echo "$enable_embedded" >&6; } if test "$enable_embedded" = "yes"; then EMBEDDED_TRUE= EMBEDDED_FALSE='#' else EMBEDDED_TRUE='#' EMBEDDED_FALSE= fi if test "$enable_embedded" = "yes"; then AM_CPPFLAGS=-DEMBEDDED fi # check whether to build OpenMP support if false; then OPENMP_TRUE= OPENMP_FALSE='#' else OPENMP_TRUE='#' OPENMP_FALSE= fi OPENMP_CXXFLAGS= # Check whether --enable-openmp was given. if test "${enable_openmp+set}" = set; then : enableval=$enable_openmp; fi if test "$enable_openmp" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CXX option to support OpenMP" >&5 $as_echo_n "checking for $CXX option to support OpenMP... " >&6; } if ${ac_cv_prog_cxx_openmp+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef _OPENMP choke me #endif #include int main () { return omp_get_num_threads (); } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_prog_cxx_openmp='none needed' else ac_cv_prog_cxx_openmp='unsupported' for ac_option in -fopenmp -xopenmp -openmp -mp -omp -qsmp=omp -homp \ -Popenmp --openmp; do ac_save_CXXFLAGS=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ac_option" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef _OPENMP choke me #endif #include int main () { return omp_get_num_threads (); } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_prog_cxx_openmp=$ac_option fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CXXFLAGS=$ac_save_CXXFLAGS if test "$ac_cv_prog_cxx_openmp" != unsupported; then break fi done fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_openmp" >&5 $as_echo "$ac_cv_prog_cxx_openmp" >&6; } case $ac_cv_prog_cxx_openmp in #( "none needed" | unsupported) ;; #( *) OPENMP_CXXFLAGS=$ac_cv_prog_cxx_openmp ;; esac fi if test "x$OPENMP_CFLAGS" != "x"; then : if true; then OPENMP_TRUE= OPENMP_FALSE='#' else OPENMP_TRUE='#' OPENMP_FALSE= fi AM_CPPFLAGS="$OPENMP_CXXFLAGS" $as_echo "#define OPENMP /**/" >>confdefs.h fi # check whether to build opencl version { $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-opencl argument" >&5 $as_echo_n "checking --enable-opencl argument... " >&6; } # Check whether --enable-opencl was given. if test "${enable_opencl+set}" = set; then : enableval=$enable_opencl; enable_opencl=$enableval else enable_opencl="no" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_opencl" >&5 $as_echo "$enable_opencl" >&6; } # check for opencl header have_opencl=false ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 $as_echo_n "checking how to run the C++ preprocessor... " >&6; } if test -z "$CXXCPP"; then if ${ac_cv_prog_CXXCPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CXXCPP needs to be expanded for CXXCPP in "$CXX -E" "/lib/cpp" do ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CXXCPP=$CXXCPP fi CXXCPP=$ac_cv_prog_CXXCPP else ac_cv_prog_CXXCPP=$CXXCPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 $as_echo "$CXXCPP" >&6; } ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_cxx_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_cxx_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in CL/cl.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "CL/cl.h" "ac_cv_header_CL_cl_h" "$ac_includes_default" if test "x$ac_cv_header_CL_cl_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_CL_CL_H 1 _ACEOF have_opencl=true else for ac_header in OpenCL/cl.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "OpenCL/cl.h" "ac_cv_header_OpenCL_cl_h" "$ac_includes_default" if test "x$ac_cv_header_OpenCL_cl_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_OPENCL_CL_H 1 _ACEOF have_opencl=true else have_opencl=false fi done fi done have_tiff=false for ac_header in tiffio.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "tiffio.h" "ac_cv_header_tiffio_h" "$ac_includes_default" if test "x$ac_cv_header_tiffio_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_TIFFIO_H 1 _ACEOF have_tiff=true else have_tiff=false fi done # https://lists.apple.com/archives/unix-porting/2009/Jan/msg00026.html have_opencl_lib=false OPENCL_CPPFLAGS='' OPENCL_LDFLAGS='' case "${host_os}" in *darwin* | *-macos10*) echo "checking for OpenCL framework" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if -framework OpenCL works" >&5 $as_echo_n "checking if -framework OpenCL works... " >&6; } if ${my_cv_framework_OpenCL+:} false; then : $as_echo_n "(cached) " >&6 else save_LIBS="$LIBS" LIBS="$LIBS -framework OpenCL" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : my_cv_framework_OpenCL=yes else my_cv_framework_OpenCL=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$save_LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $my_cv_framework_OpenCL" >&5 $as_echo "$my_cv_framework_OpenCL" >&6; } if test "$my_cv_framework_OpenCL"="yes"; then $as_echo "#define HAVE_FRAMEWORK_OPENCL 1" >>confdefs.h FRAMEWORK_OPENCL="-framework OpenCL" fi if test $my_cv_framework_OpenCL = yes; then have_opencl_lib=true fi if test "$enable_opencl" = "yes"; then if !($have_opencl_lib); then as_fn_error $? "Required OpenCL library not found!" "$LINENO" 5 fi AM_CPPFLAGS=-DUSE_OPENCL OPENCL_CPPFLAGS="" OPENCL_LDFLAGS="-framework OpenCL" fi ;; *) # default { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clGetPlatformIDs in -lOpenCL" >&5 $as_echo_n "checking for clGetPlatformIDs in -lOpenCL... " >&6; } if ${ac_cv_lib_OpenCL_clGetPlatformIDs+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lOpenCL $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char clGetPlatformIDs (); int main () { return clGetPlatformIDs (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_OpenCL_clGetPlatformIDs=yes else ac_cv_lib_OpenCL_clGetPlatformIDs=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_OpenCL_clGetPlatformIDs" >&5 $as_echo "$ac_cv_lib_OpenCL_clGetPlatformIDs" >&6; } if test "x$ac_cv_lib_OpenCL_clGetPlatformIDs" = xyes; then : have_opencl_lib=true else have_opencl_lib=false fi if test "$enable_opencl" = "yes"; then if !($have_opencl); then as_fn_error $? "Required OpenCL headers not found!" "$LINENO" 5 fi if !($have_opencl_lib); then as_fn_error $? "Required OpenCL library not found!" "$LINENO" 5 fi if !($have_tiff); then as_fn_error $? "Required TIFF headers not found! Try to install libtiff-dev?? package." "$LINENO" 5 fi AM_CPPFLAGS=-DUSE_OPENCL OPENCL_CPPFLAGS="-I${OPENCL_INC}" OPENCL_LDFLAGS="-l${OPENCL_LIBS}" fi ;; esac if test "$enable_opencl" = "yes"; then USE_OPENCL_TRUE= USE_OPENCL_FALSE='#' else USE_OPENCL_TRUE='#' USE_OPENCL_FALSE= fi # check whether to build tesseract with -fvisibility=hidden -fvisibility-inlines-hidden # http://gcc.gnu.org/wiki/Visibility # http://groups.google.com/group/tesseract-dev/browse_thread/thread/976645ae98189127 { $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-visibility argument" >&5 $as_echo_n "checking --enable-visibility argument... " >&6; } # Check whether --enable-visibility was given. if test "${enable_visibility+set}" = set; then : enableval=$enable_visibility; enable_visibility=$enableval else enable_visibility="no" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_visibility" >&5 $as_echo "$enable_visibility" >&6; } if test "$enable_visibility" = "yes"; then VISIBILITY_TRUE= VISIBILITY_FALSE='#' else VISIBILITY_TRUE='#' VISIBILITY_FALSE= fi # check whether to build multiple libraries { $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-multiple-libraries argument" >&5 $as_echo_n "checking --enable-multiple-libraries argument... " >&6; } # Check whether --enable-multiple-libraries was given. if test "${enable_multiple_libraries+set}" = set; then : enableval=$enable_multiple_libraries; enable_mlibs=$enableval else enable_mlibs="no" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_mlibs" >&5 $as_echo "$enable_mlibs" >&6; } if test "$enable_mlibs" = "yes"; then USING_MULTIPLELIBS_TRUE= USING_MULTIPLELIBS_FALSE='#' else USING_MULTIPLELIBS_TRUE='#' USING_MULTIPLELIBS_FALSE= fi # Check if tessdata-prefix is disabled { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use tessdata-prefix" >&5 $as_echo_n "checking whether to use tessdata-prefix... " >&6; } # Check whether --enable-tessdata-prefix was given. if test "${enable_tessdata_prefix+set}" = set; then : enableval=$enable_tessdata_prefix; tessdata_prefix="no" else tessdata_prefix="yes" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tessdata_prefix" >&5 $as_echo "$tessdata_prefix" >&6; } if test "$tessdata_prefix" = "no"; then NO_TESSDATA_PREFIX_TRUE= NO_TESSDATA_PREFIX_FALSE='#' else NO_TESSDATA_PREFIX_TRUE='#' NO_TESSDATA_PREFIX_FALSE= fi # Check whether enable debuging { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable debugging" >&5 $as_echo_n "checking whether to enable debugging... " >&6; } # Check whether --enable-debug was given. if test "${enable_debug+set}" = set; then : enableval=$enable_debug; debug=$enableval else debug="no" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $debug" >&5 $as_echo "$debug" >&6; } if test x"$debug" = x"yes"; then AM_CXXFLAGS="$AM_CXXFLAGS -g -Wall -Wno-uninitialized -O0 -DDEBUG" AM_CPPFLAGS="$AM_CPPFLAGS -g -Wall -Wno-uninitialized -O0 -DDEBUG" else AM_CXXFLAGS="$AM_CXXFLAGS -O2 -DNDEBUG" AM_CPPFLAGS="$AM_CPPFLAGS -O2 -DNDEBUG" fi #localedir='${prefix}/share/locale' # Always look into a "gnu" directory. curwd=`pwd` if test -d $curwd/gnu/include ; then CPPFLAGS="$CPPFLAGS -I$curwd/gnu/include" fi if test -d $curwd/gnu/lib ; then LDFLAGS="$LDFLAGS -L$curwd/gnu/lib" fi # ---------------------------------------- # Check Compiler Characteristics and # configure automake. The two appear to # be intimately linked... # ---------------------------------------- case `pwd` in *\ * | *\ *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 $as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; esac macro_version='2.4.2' macro_revision='1.3337' ltmain="$ac_aux_dir/ltmain.sh" # Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\(["`$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 $as_echo_n "checking how to print strings... " >&6; } # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "" } case "$ECHO" in printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 $as_echo "printf" >&6; } ;; print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 $as_echo "print -r" >&6; } ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 $as_echo "cat" >&6; } ;; esac ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 $as_echo_n "checking for a sed that does not truncate output... " >&6; } if ${ac_cv_path_SED+:} false; then : $as_echo_n "(cached) " >&6 else ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_SED" || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 $as_echo "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 $as_echo_n "checking for fgrep... " >&6; } if ${ac_cv_path_FGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 then ac_cv_path_FGREP="$GREP -F" else if test -z "$FGREP"; then ac_path_FGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in fgrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_FGREP" || continue # Check for GNU ac_path_FGREP and select it if it is found. # Check for GNU $ac_path_FGREP case `"$ac_path_FGREP" --version 2>&1` in *GNU*) ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'FGREP' >> "conftest.nl" "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_FGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_FGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_FGREP"; then as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_FGREP=$FGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 $as_echo "$ac_cv_path_FGREP" >&6; } FGREP="$ac_cv_path_FGREP" test -z "$GREP" && GREP=grep # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if ${lt_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if ${lt_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 $as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } if ${lt_cv_path_NM+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM="$NM" else lt_nm_to_check="${ac_tool_prefix}nm" if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. tmp_nm="$ac_dir/$lt_tmp_nm" if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then # Check to see if the nm accepts a BSD-compat flag. # Adding the `sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in */dev/null* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS="$lt_save_ifs" done : ${lt_cv_path_NM=no} fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 $as_echo "$lt_cv_path_NM" >&6; } if test "$lt_cv_path_NM" != "no"; then NM="$lt_cv_path_NM" else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else if test -n "$ac_tool_prefix"; then for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DUMPBIN"; then ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DUMPBIN=$ac_cv_prog_DUMPBIN if test -n "$DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 $as_echo "$DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$DUMPBIN" && break done fi if test -z "$DUMPBIN"; then ac_ct_DUMPBIN=$DUMPBIN for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DUMPBIN"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN if test -n "$ac_ct_DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 $as_echo "$ac_ct_DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_DUMPBIN" && break done if test "x$ac_ct_DUMPBIN" = x; then DUMPBIN=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DUMPBIN=$ac_ct_DUMPBIN fi fi case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols" ;; *) DUMPBIN=: ;; esac fi if test "$DUMPBIN" != ":"; then NM="$DUMPBIN" fi fi test -z "$NM" && NM=nm { $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 $as_echo_n "checking the name lister ($NM) interface... " >&6; } if ${lt_cv_nm_interface+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 $as_echo "$lt_cv_nm_interface" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 $as_echo "no, using $LN_S" >&6; } fi # find the maximum length of command line arguments { $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 $as_echo_n "checking the maximum length of command line arguments... " >&6; } if ${lt_cv_sys_max_cmd_len+:} false; then : $as_echo_n "(cached) " >&6 else i=0 teststring="ABCD" case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8 ; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test $i != 17 # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac fi if test -n $lt_cv_sys_max_cmd_len ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 $as_echo "$lt_cv_sys_max_cmd_len" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 $as_echo "none" >&6; } fi max_cmd_len=$lt_cv_sys_max_cmd_len : ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5 $as_echo_n "checking whether the shell understands some XSI constructs... " >&6; } # Try some XSI features xsi_shell=no ( _lt_dummy="a/b/c" test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ = c,a/b,b/c, \ && eval 'test $(( 1 + 1 )) -eq 2 \ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ && xsi_shell=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5 $as_echo "$xsi_shell" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5 $as_echo_n "checking whether the shell understands \"+=\"... " >&6; } lt_shell_append=no ( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \ >/dev/null 2>&1 \ && lt_shell_append=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5 $as_echo "$lt_shell_append" >&6; } if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 $as_echo_n "checking how to convert $build file names to $host format... " >&6; } if ${lt_cv_to_host_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac fi to_host_file_cmd=$lt_cv_to_host_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 $as_echo "$lt_cv_to_host_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 $as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } if ${lt_cv_to_tool_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else #assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac fi to_tool_file_cmd=$lt_cv_to_tool_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 $as_echo "$lt_cv_to_tool_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 $as_echo_n "checking for $LD option to reload object files... " >&6; } if ${lt_cv_ld_reload_flag+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_reload_flag='-r' fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 $as_echo "$lt_cv_ld_reload_flag" >&6; } reload_flag=$lt_cv_ld_reload_flag case $reload_flag in "" | " "*) ;; *) reload_flag=" $reload_flag" ;; esac reload_cmds='$LD$reload_flag -o $output$reload_objs' case $host_os in cygwin* | mingw* | pw32* | cegcc*) if test "$GCC" != yes; then reload_cmds=false fi ;; darwin*) if test "$GCC" = yes; then reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' else reload_cmds='$LD$reload_flag -o $output$reload_objs' fi ;; esac if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. set dummy ${ac_tool_prefix}objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OBJDUMP"; then ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OBJDUMP=$ac_cv_prog_OBJDUMP if test -n "$OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 $as_echo "$OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OBJDUMP"; then ac_ct_OBJDUMP=$OBJDUMP # Extract the first word of "objdump", so it can be a program name with args. set dummy objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OBJDUMP"; then ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OBJDUMP="objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP if test -n "$ac_ct_OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 $as_echo "$ac_ct_OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OBJDUMP" = x; then OBJDUMP="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OBJDUMP=$ac_ct_OBJDUMP fi else OBJDUMP="$ac_cv_prog_OBJDUMP" fi test -z "$OBJDUMP" && OBJDUMP=objdump { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 $as_echo_n "checking how to recognize dependent libraries... " >&6; } if ${lt_cv_deplibs_check_method+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # `unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # which responds to the $file_magic_cmd with a given extended regex. # If you have `file' or equivalent on your system and you're not sure # whether `pass_all' will *always* work, you probably want this one. case $host_os in aix[4-9]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[45]*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin. if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; gnu*) lt_cv_deplibs_check_method=pass_all ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[3-9]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu) lt_cv_deplibs_check_method=pass_all ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 $as_echo "$lt_cv_deplibs_check_method" >&6; } file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. set dummy ${ac_tool_prefix}dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DLLTOOL"; then ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DLLTOOL=$ac_cv_prog_DLLTOOL if test -n "$DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 $as_echo "$DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DLLTOOL"; then ac_ct_DLLTOOL=$DLLTOOL # Extract the first word of "dlltool", so it can be a program name with args. set dummy dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DLLTOOL"; then ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DLLTOOL="dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL if test -n "$ac_ct_DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 $as_echo "$ac_ct_DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DLLTOOL" = x; then DLLTOOL="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DLLTOOL=$ac_ct_DLLTOOL fi else DLLTOOL="$ac_cv_prog_DLLTOOL" fi test -z "$DLLTOOL" && DLLTOOL=dlltool { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 $as_echo_n "checking how to associate runtime and link libraries... " >&6; } if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh # decide which to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd="$ECHO" ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 $as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO if test -n "$ac_tool_prefix"; then for ac_prog in ar do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi : ${AR=ar} : ${AR_FLAGS=cru} { $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 $as_echo_n "checking for archiver @FILE support... " >&6; } if ${lt_cv_ar_at_file+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ar_at_file=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test "$ac_status" -eq 0; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test "$ac_status" -ne 0; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 $as_echo "$lt_cv_ar_at_file" >&6; } if test "x$lt_cv_ar_at_file" = xno; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi test -z "$STRIP" && STRIP=: if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi test -z "$RANLIB" && RANLIB=: # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Check for command to grab the raw symbol name followed by C symbol from nm. { $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 $as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } if ${lt_cv_sys_global_symbol_pipe+:} false; then : $as_echo_n "(cached) " >&6 else # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[BCDEGRST]' # Regexp to match symbols that can be accessed directly from C. sympat='\([_A-Za-z][_A-Za-z0-9]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[BCDT]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[ABCDGISTW]' ;; hpux*) if test "$host_cpu" = ia64; then symcode='[ABCDEGRST]' fi ;; irix* | nonstopux*) symcode='[BCDEGRST]' ;; osf*) symcode='[BCDEGQRST]' ;; solaris*) symcode='[BDRT]' ;; sco3.2v5*) symcode='[DT]' ;; sysv4.2uw2*) symcode='[DT]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[ABDT]' ;; sysv4) symcode='[DFNSTU]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[ABCDGIRSTW]' ;; esac # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'" lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function # and D for any global variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK '"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ " {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ " s[1]~/^[@?]/{print s[1], s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Now try to grab the symbols. nlist=conftest.nm if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) /* DATA imports from DLLs on WIN32 con't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined(__osf__) /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS="conftstm.$ac_objext" CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest${ac_exeext}; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&5 fi else echo "cannot find nm_test_var in $nlist" >&5 fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 fi else echo "$progname: failed program was:" >&5 cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test "$pipe_works" = yes; then break else lt_cv_sys_global_symbol_pipe= fi done fi if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 $as_echo "failed" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then nm_file_list_spec='@' fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 $as_echo_n "checking for sysroot... " >&6; } # Check whether --with-sysroot was given. if test "${with_sysroot+set}" = set; then : withval=$with_sysroot; else with_sysroot=no fi lt_sysroot= case ${with_sysroot} in #( yes) if test "$GCC" = yes; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_sysroot}" >&5 $as_echo "${with_sysroot}" >&6; } as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 $as_echo "${lt_sysroot:-no}" >&6; } # Check whether --enable-libtool-lock was given. if test "${enable_libtool_lock+set}" = set; then : enableval=$enable_libtool_lock; fi test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE="32" ;; *ELF-64*) HPUX_IA64_MODE="64" ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out which ABI we are using. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then if test "$lt_cv_prog_gnu_ld" = yes; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_i386" ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -belf" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 $as_echo_n "checking whether the C compiler needs -belf... " >&6; } if ${lt_cv_cc_needs_belf+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_cc_needs_belf=yes else lt_cv_cc_needs_belf=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 $as_echo "$lt_cv_cc_needs_belf" >&6; } if test x"$lt_cv_cc_needs_belf" != x"yes"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS="$SAVE_CFLAGS" fi ;; *-*solaris*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD="${LD-ld}_sol2" fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks="$enable_libtool_lock" if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. set dummy ${ac_tool_prefix}mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$MANIFEST_TOOL"; then ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL if test -n "$MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 $as_echo "$MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_MANIFEST_TOOL"; then ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL # Extract the first word of "mt", so it can be a program name with args. set dummy mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_MANIFEST_TOOL"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL if test -n "$ac_ct_MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 $as_echo "$ac_ct_MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_MANIFEST_TOOL" = x; then MANIFEST_TOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL fi else MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" fi test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 $as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } if ${lt_cv_path_mainfest_tool+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&5 if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 $as_echo "$lt_cv_path_mainfest_tool" >&6; } if test "x$lt_cv_path_mainfest_tool" != xyes; then MANIFEST_TOOL=: fi case $host_os in rhapsody* | darwin*) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DSYMUTIL"; then ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DSYMUTIL=$ac_cv_prog_DSYMUTIL if test -n "$DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 $as_echo "$DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DSYMUTIL"; then ac_ct_DSYMUTIL=$DSYMUTIL # Extract the first word of "dsymutil", so it can be a program name with args. set dummy dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DSYMUTIL"; then ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL if test -n "$ac_ct_DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 $as_echo "$ac_ct_DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DSYMUTIL" = x; then DSYMUTIL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DSYMUTIL=$ac_ct_DSYMUTIL fi else DSYMUTIL="$ac_cv_prog_DSYMUTIL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. set dummy ${ac_tool_prefix}nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NMEDIT"; then ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi NMEDIT=$ac_cv_prog_NMEDIT if test -n "$NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 $as_echo "$NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_NMEDIT"; then ac_ct_NMEDIT=$NMEDIT # Extract the first word of "nmedit", so it can be a program name with args. set dummy nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_NMEDIT"; then ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_NMEDIT="nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT if test -n "$ac_ct_NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 $as_echo "$ac_ct_NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_NMEDIT" = x; then NMEDIT=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac NMEDIT=$ac_ct_NMEDIT fi else NMEDIT="$ac_cv_prog_NMEDIT" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. set dummy ${ac_tool_prefix}lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$LIPO"; then ac_cv_prog_LIPO="$LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_LIPO="${ac_tool_prefix}lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi LIPO=$ac_cv_prog_LIPO if test -n "$LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 $as_echo "$LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_LIPO"; then ac_ct_LIPO=$LIPO # Extract the first word of "lipo", so it can be a program name with args. set dummy lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_LIPO"; then ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_LIPO="lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO if test -n "$ac_ct_LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 $as_echo "$ac_ct_LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_LIPO" = x; then LIPO=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac LIPO=$ac_ct_LIPO fi else LIPO="$ac_cv_prog_LIPO" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. set dummy ${ac_tool_prefix}otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL"; then ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL="${ac_tool_prefix}otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL=$ac_cv_prog_OTOOL if test -n "$OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 $as_echo "$OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL"; then ac_ct_OTOOL=$OTOOL # Extract the first word of "otool", so it can be a program name with args. set dummy otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL"; then ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL="otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL if test -n "$ac_ct_OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 $as_echo "$ac_ct_OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL" = x; then OTOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL=$ac_ct_OTOOL fi else OTOOL="$ac_cv_prog_OTOOL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. set dummy ${ac_tool_prefix}otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL64"; then ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL64=$ac_cv_prog_OTOOL64 if test -n "$OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 $as_echo "$OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL64"; then ac_ct_OTOOL64=$OTOOL64 # Extract the first word of "otool64", so it can be a program name with args. set dummy otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL64"; then ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL64="otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 if test -n "$ac_ct_OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 $as_echo "$ac_ct_OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL64" = x; then OTOOL64=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL64=$ac_ct_OTOOL64 fi else OTOOL64="$ac_cv_prog_OTOOL64" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 $as_echo_n "checking for -single_module linker flag... " >&6; } if ${lt_cv_apple_cc_single_mod+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_apple_cc_single_mod=no if test -z "${LT_MULTI_MODULE}"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&5 $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&5 # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test $_lt_result -eq 0; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&5 fi rm -rf libconftest.dylib* rm -f conftest.* fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 $as_echo "$lt_cv_apple_cc_single_mod" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 $as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } if ${lt_cv_ld_exported_symbols_list+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_ld_exported_symbols_list=yes else lt_cv_ld_exported_symbols_list=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS="$save_LDFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 $as_echo "$lt_cv_ld_exported_symbols_list" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 $as_echo_n "checking for -force_load linker flag... " >&6; } if ${lt_cv_ld_force_load+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 echo "$AR cru libconftest.a conftest.o" >&5 $AR cru libconftest.a conftest.o 2>&5 echo "$RANLIB libconftest.a" >&5 $RANLIB libconftest.a 2>&5 cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&5 elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then lt_cv_ld_force_load=yes else cat conftest.err >&5 fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 $as_echo "$lt_cv_ld_force_load" >&6; } case $host_os in rhapsody* | darwin1.[012]) _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[91]*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; 10.[012]*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test "$lt_cv_apple_cc_single_mod" = "yes"; then _lt_dar_single_mod='$single_module' fi if test "$lt_cv_ld_exported_symbols_list" = "yes"; then _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' fi if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac for ac_header in dlfcn.h do : ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default " if test "x$ac_cv_header_dlfcn_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_DLFCN_H 1 _ACEOF fi done func_stripname_cnf () { case ${2} in .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; esac } # func_stripname_cnf # Set options enable_dlopen=no enable_win32_dll=no # Check whether --enable-shared was given. if test "${enable_shared+set}" = set; then : enableval=$enable_shared; p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS="$lt_save_ifs" ;; esac else enable_shared=yes fi # Check whether --enable-static was given. if test "${enable_static+set}" = set; then : enableval=$enable_static; p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS="$lt_save_ifs" ;; esac else enable_static=yes fi # Check whether --with-pic was given. if test "${with_pic+set}" = set; then : withval=$with_pic; lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for lt_pkg in $withval; do IFS="$lt_save_ifs" if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS="$lt_save_ifs" ;; esac else pic_mode=default fi test -z "$pic_mode" && pic_mode=default # Check whether --enable-fast-install was given. if test "${enable_fast_install+set}" = set; then : enableval=$enable_fast_install; p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS="$lt_save_ifs" ;; esac else enable_fast_install=yes fi # This can be used to rebuild libtool when needed LIBTOOL_DEPS="$ltmain" # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' test -z "$LN_S" && LN_S="ln -s" if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 $as_echo_n "checking for objdir... " >&6; } if ${lt_cv_objdir+:} false; then : $as_echo_n "(cached) " >&6 else rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 $as_echo "$lt_cv_objdir" >&6; } objdir=$lt_cv_objdir cat >>confdefs.h <<_ACEOF #define LT_OBJDIR "$lt_cv_objdir/" _ACEOF case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld="$lt_cv_prog_gnu_ld" old_CC="$CC" old_CFLAGS="$CFLAGS" # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o for cc_temp in $compiler""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 $as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD="$MAGIC_CMD" lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/${ac_tool_prefix}file; then lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS="$lt_save_ifs" MAGIC_CMD="$lt_save_MAGIC_CMD" ;; esac fi MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 $as_echo_n "checking for file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD="$MAGIC_CMD" lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/file; then lt_cv_path_MAGIC_CMD="$ac_dir/file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS="$lt_save_ifs" MAGIC_CMD="$lt_save_MAGIC_CMD" ;; esac fi MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi else MAGIC_CMD=: fi fi fi ;; esac # Use C for the default configuration in the libtool script lt_save_CC="$CC" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o objext=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then lt_prog_compiler_no_builtin_flag= if test "$GCC" = yes; then case $cc_basename in nvcc*) lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; *) lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 $as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_rtti_exceptions=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-fno-rtti -fno-exceptions" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_rtti_exceptions=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 $as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" else : fi fi lt_prog_compiler_wl= lt_prog_compiler_pic= lt_prog_compiler_static= if test "$GCC" = yes; then lt_prog_compiler_wl='-Wl,' lt_prog_compiler_static='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic='-DDLL_EXPORT' ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) lt_prog_compiler_pic='-fPIC' ;; esac ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. lt_prog_compiler_can_build_shared=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic=-Kconform_pic fi ;; *) lt_prog_compiler_pic='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 lt_prog_compiler_wl='-Xlinker ' if test -n "$lt_prog_compiler_pic"; then lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) lt_prog_compiler_wl='-Wl,' if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' else lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' fi ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic='-DDLL_EXPORT' ;; hpux9* | hpux10* | hpux11*) lt_prog_compiler_wl='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? lt_prog_compiler_static='${wl}-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) lt_prog_compiler_wl='-Wl,' # PIC (with -KPIC) is the default. lt_prog_compiler_static='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in # old Intel for x86_64 which still supported -KPIC. ecc*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; # Lahey Fortran 8.1. lf95*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='--shared' lt_prog_compiler_static='--static' ;; nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; ccc*) lt_prog_compiler_wl='-Wl,' # All Alpha code is PIC. lt_prog_compiler_static='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-qpic' lt_prog_compiler_static='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='' ;; *Sun\ F* | *Sun*Fortran*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Wl,' ;; *Intel*\ [CF]*Compiler*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; *Portland\ Group*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; esac ;; esac ;; newsos6) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; osf3* | osf4* | osf5*) lt_prog_compiler_wl='-Wl,' # All OSF/1 code is PIC. lt_prog_compiler_static='-non_shared' ;; rdos*) lt_prog_compiler_static='-non_shared' ;; solaris*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) lt_prog_compiler_wl='-Qoption ld ';; *) lt_prog_compiler_wl='-Wl,';; esac ;; sunos4*) lt_prog_compiler_wl='-Qoption ld ' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec ;then lt_prog_compiler_pic='-Kconform_pic' lt_prog_compiler_static='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; unicos*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_can_build_shared=no ;; uts4*) lt_prog_compiler_pic='-pic' lt_prog_compiler_static='-Bstatic' ;; *) lt_prog_compiler_can_build_shared=no ;; esac fi case $host_os in # For platforms which do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic= ;; *) lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } if ${lt_cv_prog_compiler_pic+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic=$lt_prog_compiler_pic fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 $as_echo "$lt_cv_prog_compiler_pic" >&6; } lt_prog_compiler_pic=$lt_cv_prog_compiler_pic # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } if ${lt_cv_prog_compiler_pic_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic -DPIC" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 $as_echo "$lt_cv_prog_compiler_pic_works" >&6; } if test x"$lt_cv_prog_compiler_pic_works" = xyes; then case $lt_prog_compiler_pic in "" | " "*) ;; *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; esac else lt_prog_compiler_pic= lt_prog_compiler_can_build_shared=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if ${lt_cv_prog_compiler_static_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works=no save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works=yes fi else lt_cv_prog_compiler_static_works=yes fi fi $RM -r conftest* LDFLAGS="$save_LDFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 $as_echo "$lt_cv_prog_compiler_static_works" >&6; } if test x"$lt_cv_prog_compiler_static_works" = xyes; then : else lt_prog_compiler_static= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } hard_links="nottested" if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test "$hard_links" = no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } runpath_var= allow_undefined_flag= always_export_symbols=no archive_cmds= archive_expsym_cmds= compiler_needs_object=no enable_shared_with_static_runtimes=no export_dynamic_flag_spec= export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' hardcode_automatic=no hardcode_direct=no hardcode_direct_absolute=no hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_minus_L=no hardcode_shlibpath_var=unsupported inherit_rpath=no link_all_deplibs=unknown module_cmds= module_expsym_cmds= old_archive_from_new_cmds= old_archive_from_expsyms_cmds= thread_safe_flag_spec= whole_archive_flag_spec= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list include_expsyms= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ` (' and `)$', so one must not match beginning or # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', # as well as any symbol that contains `d'. exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; esac ld_shlibs=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test "$with_gnu_ld" = yes; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; *\ \(GNU\ Binutils\)\ [3-9]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test "$lt_use_gnu_ld_interface" = yes; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='${wl}' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' export_dynamic_flag_spec='${wl}--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else whole_archive_flag_spec= fi supports_anon_versioning=no case `$LD -v 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else ld_shlibs=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' export_dynamic_flag_spec='${wl}--export-all-symbols' allow_undefined_flag=unsupported always_export_symbols=no enable_shared_with_static_runtimes=yes export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs=no fi ;; haiku*) archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' link_all_deplibs=yes ;; interix[3-9]*) hardcode_direct=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='${wl}-rpath,$libdir' export_dynamic_flag_spec='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test "$host_os" = linux-dietlibc; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test "$tmp_diet" = no then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 whole_archive_flag_spec= tmp_sharedflag='--shared' ;; xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' compiler_needs_object=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' compiler_needs_object=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi case $cc_basename in xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test "x$supports_anon_versioning" = xyes; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else ld_shlibs=no fi ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac ;; sunos4*) archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= hardcode_direct=yes hardcode_shlibpath_var=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac if test "$ld_shlibs" = no; then runpath_var= hardcode_libdir_flag_spec= export_dynamic_flag_spec= whole_archive_flag_spec= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) allow_undefined_flag=unsupported always_export_symbols=yes archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm # Also, AIX nm treats weak defined symbols like other global # defined symbols, whereas GNU nm marks them as "W". if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds='' hardcode_direct=yes hardcode_direct_absolute=yes hardcode_libdir_separator=':' link_all_deplibs=yes file_list_spec='${wl}-f,' if test "$GCC" = yes; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi export_dynamic_flag_spec='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. always_export_symbols=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. allow_undefined_flag='-berok' # Determine the default libpath from the value encoded in an # empty executable. if test "${lt_cv_aix_libpath+set}" = set; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_="/usr/lib:/lib" fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' allow_undefined_flag="-z nodefs" archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test "${lt_cv_aix_libpath+set}" = set; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_="/usr/lib:/lib" fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag=' ${wl}-bernotok' allow_undefined_flag=' ${wl}-berok' if test "$with_gnu_ld" = yes; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec='$convenience' fi archive_cmds_need_lc=yes # This is similar to how AIX traditionally builds its shared libraries. archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; bsdi[45]*) export_dynamic_flag_spec=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported always_export_symbols=yes file_list_spec='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; else sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, )='true' enable_shared_with_static_runtimes=yes exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib old_postinstall_cmds='chmod 644 $oldlib' postlink_cmds='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile="$lt_outputfile.exe" lt_tool_outputfile="$lt_tool_outputfile.exe" ;; esac~ if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. old_archive_from_new_cmds='true' # FIXME: Should let the user specify the lib program. old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' enable_shared_with_static_runtimes=yes ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc=no hardcode_direct=no hardcode_automatic=yes hardcode_shlibpath_var=unsupported if test "$lt_cv_ld_force_load" = "yes"; then whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec='' fi link_all_deplibs=yes allow_undefined_flag="$_lt_dar_allow_undefined" case $cc_basename in ifort*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test "$_lt_dar_can_shared" = "yes"; then output_verbose_link_cmd=func_echo_all archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" else ld_shlibs=no fi ;; dgux*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; hpux9*) if test "$GCC" = yes; then archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' fi hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes export_dynamic_flag_spec='${wl}-E' ;; hpux10*) if test "$GCC" = yes && test "$with_gnu_ld" = no; then archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test "$GCC" = yes && test "$with_gnu_ld" = no; then case $host_cpu in hppa*64*) archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 $as_echo_n "checking if $CC understands -b... " >&6; } if ${lt_cv_prog_compiler__b+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler__b=no save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -b" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler__b=yes fi else lt_cv_prog_compiler__b=yes fi fi $RM -r conftest* LDFLAGS="$save_LDFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 $as_echo "$lt_cv_prog_compiler__b" >&6; } if test x"$lt_cv_prog_compiler__b" = xyes; then archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi ;; esac fi if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no hardcode_shlibpath_var=no ;; *) hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test "$GCC" = yes; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 $as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } if ${lt_cv_irix_exported_symbol+:} false; then : $as_echo_n "(cached) " >&6 else save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int foo (void) { return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_irix_exported_symbol=yes else lt_cv_irix_exported_symbol=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS="$save_LDFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 $as_echo "$lt_cv_irix_exported_symbol" >&6; } if test "$lt_cv_irix_exported_symbol" = yes; then archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' fi else archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: inherit_rpath=yes link_all_deplibs=yes ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; newsos6) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: hardcode_shlibpath_var=no ;; *nto* | *qnx*) ;; openbsd*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes hardcode_shlibpath_var=no hardcode_direct_absolute=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' hardcode_libdir_flag_spec='${wl}-rpath,$libdir' export_dynamic_flag_spec='${wl}-E' else case $host_os in openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-R$libdir' ;; *) archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; esac fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' ;; osf3*) if test "$GCC" = yes; then allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test "$GCC" = yes; then allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' archive_cmds='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi archive_cmds_need_lc='no' hardcode_libdir_separator=: ;; solaris*) no_undefined_flag=' -z defs' if test "$GCC" = yes; then wlarc='${wl}' archive_cmds='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='${wl}' archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi hardcode_libdir_flag_spec='-R$libdir' hardcode_shlibpath_var=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. GCC discards it without `$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test "$GCC" = yes; then whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' else whole_archive_flag_spec='-z allextract$convenience -z defaultextract' fi ;; esac link_all_deplibs=yes ;; sunos4*) if test "x$host_vendor" = xsequent; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; sysv4) case $host_vendor in sni) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' reload_cmds='$CC -r -o $output$reload_objs' hardcode_direct=no ;; motorola) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' hardcode_shlibpath_var=no ;; sysv4.3*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no export_dynamic_flag_spec='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag='${wl}-z,text' archive_cmds_need_lc=no hardcode_shlibpath_var=no runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag='${wl}-z,text' allow_undefined_flag='${wl}-z,nodefs' archive_cmds_need_lc=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='${wl}-R,$libdir' hardcode_libdir_separator=':' link_all_deplibs=yes export_dynamic_flag_spec='${wl}-Bexport' runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; *) ld_shlibs=no ;; esac if test x$host_vendor = xsni; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) export_dynamic_flag_spec='${wl}-Blargedynsym' ;; esac fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 $as_echo "$ld_shlibs" >&6; } test "$ld_shlibs" = no && can_build_shared=no with_gnu_ld=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc" in x|xyes) # Assume -lc should be added archive_cmds_need_lc=yes if test "$enable_shared" = yes && test "$GCC" = yes; then case $archive_cmds in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } if ${lt_cv_archive_cmds_need_lc+:} false; then : $as_echo_n "(cached) " >&6 else $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl pic_flag=$lt_prog_compiler_pic compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag allow_undefined_flag= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc=no else lt_cv_archive_cmds_need_lc=yes fi allow_undefined_flag=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 $as_echo "$lt_cv_archive_cmds_need_lc" >&6; } archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } if test "$GCC" = yes; then case $host_os in darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; *) lt_awk_arg="/^libraries:/" ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq="s,=\([A-Za-z]:\),\1,g" ;; *) lt_sed_strip_eq="s,=/,/,g" ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary. lt_tmp_lt_search_path_spec= lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path/$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" else test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS=" "; FS="/|\n";} { lt_foo=""; lt_count=0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo="/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[lt_foo]++; } if (lt_freq[lt_foo] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's,/\([A-Za-z]:\),\1,g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=".so" postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='${libname}${release}${shared_ext}$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test "$host_cpu" = ia64; then # AIX 5 supports IA64 library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line `#! .'. This would cause the generated library to # depend on `.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # AIX (on Power*) has no versioning support, so currently we can not hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. if test "$aix_use_runtimelinking" = yes; then # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' else # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='${libname}${release}.a $libname.a' soname_spec='${libname}${release}${shared_ext}$major' fi shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='${libname}${shared_ext}' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=".dll" need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' library_names_spec='${libname}.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec="$LIB" if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' soname_spec='${libname}${release}${major}$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=yes sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' if test "X$HPUX_IA64_MODE" = X32; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" fi sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test "$lt_cv_prog_gnu_ld" = yes; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if ${lt_cv_shlibpath_overrides_runpath+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Append ld.so.conf contents to the search path if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd*) version_type=sunos sys_lib_dlsearch_path_spec="/usr/lib" need_lib_prefix=no # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. case $host_os in openbsd3.3 | openbsd3.3.*) need_version=yes ;; *) need_version=no ;; esac library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then case $host_os in openbsd2.[89] | openbsd2.[89].*) shlibpath_overrides_runpath=no ;; *) shlibpath_overrides_runpath=yes ;; esac else shlibpath_overrides_runpath=yes fi ;; os2*) libname_spec='$name' shrext_cmds=".dll" need_lib_prefix=no library_names_spec='$libname${shared_ext} $libname.a' dynamic_linker='OS/2 ld.exe' shlibpath_var=LIBPATH ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test "$with_gnu_ld" = yes; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec ;then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' soname_spec='$libname${shared_ext}.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=freebsd-elf need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test "$with_gnu_ld" = yes; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test "$dynamic_linker" = no && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test "$GCC" = yes; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" fi if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action= if test -n "$hardcode_libdir_flag_spec" || test -n "$runpath_var" || test "X$hardcode_automatic" = "Xyes" ; then # We can hardcode non-existent directories. if test "$hardcode_direct" != no && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no && test "$hardcode_minus_L" != no; then # Linking always hardcodes the temporary library directory. hardcode_action=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 $as_echo "$hardcode_action" >&6; } if test "$hardcode_action" = relink || test "$inherit_rpath" = yes; then # Fast installation is not supported enable_fast_install=no elif test "$shlibpath_overrides_runpath" = yes || test "$enable_shared" = no; then # Fast installation is not necessary enable_fast_install=needless fi if test "x$enable_dlopen" != xyes; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen="load_add_on" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen="LoadLibrary" lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen="dlopen" lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" else lt_cv_dlopen="dyld" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes fi ;; *) ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" if test "x$ac_cv_func_shl_load" = xyes; then : lt_cv_dlopen="shl_load" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 $as_echo_n "checking for shl_load in -ldld... " >&6; } if ${ac_cv_lib_dld_shl_load+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char shl_load (); int main () { return shl_load (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_shl_load=yes else ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 $as_echo "$ac_cv_lib_dld_shl_load" >&6; } if test "x$ac_cv_lib_dld_shl_load" = xyes; then : lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld" else ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" if test "x$ac_cv_func_dlopen" = xyes; then : lt_cv_dlopen="dlopen" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 $as_echo_n "checking for dlopen in -lsvld... " >&6; } if ${ac_cv_lib_svld_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsvld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_svld_dlopen=yes else ac_cv_lib_svld_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 $as_echo "$ac_cv_lib_svld_dlopen" >&6; } if test "x$ac_cv_lib_svld_dlopen" = xyes; then : lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 $as_echo_n "checking for dld_link in -ldld... " >&6; } if ${ac_cv_lib_dld_dld_link+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dld_link (); int main () { return dld_link (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_dld_link=yes else ac_cv_lib_dld_dld_link=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 $as_echo "$ac_cv_lib_dld_dld_link" >&6; } if test "x$ac_cv_lib_dld_dld_link" = xyes; then : lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld" fi fi fi fi fi fi ;; esac if test "x$lt_cv_dlopen" != xno; then enable_dlopen=yes else enable_dlopen=no fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS="$CPPFLAGS" test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS="$LDFLAGS" wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS="$LIBS" LIBS="$lt_cv_dlopen_libs $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 $as_echo_n "checking whether a program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : lt_cv_dlopen_self=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisbility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; esac else : # compilation failed lt_cv_dlopen_self=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 $as_echo "$lt_cv_dlopen_self" >&6; } if test "x$lt_cv_dlopen_self" = xyes; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 $as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self_static+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : lt_cv_dlopen_self_static=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisbility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; esac else : # compilation failed lt_cv_dlopen_self_static=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 $as_echo "$lt_cv_dlopen_self_static" >&6; } fi CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi striplib= old_striplib= { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 $as_echo_n "checking whether stripping libraries is possible... " >&6; } if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP" ; then striplib="$STRIP -x" old_striplib="$STRIP -S" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; esac fi # Report which library types will actually be built { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 $as_echo_n "checking if libtool supports shared libraries... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 $as_echo "$can_build_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 $as_echo_n "checking whether to build shared libraries... " >&6; } test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[4-9]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 $as_echo "$enable_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 $as_echo_n "checking whether to build static libraries... " >&6; } # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 $as_echo "$enable_static" >&6; } fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu CC="$lt_save_CC" if test -n "$CXX" && ( test "X$CXX" != "Xno" && ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || (test "X$CXX" != "Xg++"))) ; then ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 $as_echo_n "checking how to run the C++ preprocessor... " >&6; } if test -z "$CXXCPP"; then if ${ac_cv_prog_CXXCPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CXXCPP needs to be expanded for CXXCPP in "$CXX -E" "/lib/cpp" do ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CXXCPP=$CXXCPP fi CXXCPP=$ac_cv_prog_CXXCPP else ac_cv_prog_CXXCPP=$CXXCPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 $as_echo "$CXXCPP" >&6; } ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu else _lt_caught_CXX_error=yes fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu archive_cmds_need_lc_CXX=no allow_undefined_flag_CXX= always_export_symbols_CXX=no archive_expsym_cmds_CXX= compiler_needs_object_CXX=no export_dynamic_flag_spec_CXX= hardcode_direct_CXX=no hardcode_direct_absolute_CXX=no hardcode_libdir_flag_spec_CXX= hardcode_libdir_separator_CXX= hardcode_minus_L_CXX=no hardcode_shlibpath_var_CXX=unsupported hardcode_automatic_CXX=no inherit_rpath_CXX=no module_cmds_CXX= module_expsym_cmds_CXX= link_all_deplibs_CXX=unknown old_archive_cmds_CXX=$old_archive_cmds reload_flag_CXX=$reload_flag reload_cmds_CXX=$reload_cmds no_undefined_flag_CXX= whole_archive_flag_spec_CXX= enable_shared_with_static_runtimes_CXX=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o objext_CXX=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_caught_CXX_error" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC compiler_CXX=$CC for cc_temp in $compiler""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test "$GXX" = yes; then lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' else lt_prog_compiler_no_builtin_flag_CXX= fi if test "$GXX" = yes; then # Set up default GNU C++ configuration # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if ${lt_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if ${lt_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test "$with_gnu_ld" = yes; then archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' export_dynamic_flag_spec_CXX='${wl}--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='${wl}' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else whole_archive_flag_spec_CXX= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } ld_shlibs_CXX=yes case $host_os in aix3*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aix[4-9]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds_CXX='' hardcode_direct_CXX=yes hardcode_direct_absolute_CXX=yes hardcode_libdir_separator_CXX=':' link_all_deplibs_CXX=yes file_list_spec_CXX='${wl}-f,' if test "$GXX" = yes; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct_CXX=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L_CXX=yes hardcode_libdir_flag_spec_CXX='-L$libdir' hardcode_libdir_separator_CXX= fi esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi export_dynamic_flag_spec_CXX='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. always_export_symbols_CXX=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. allow_undefined_flag_CXX='-berok' # Determine the default libpath from the value encoded in an empty # executable. if test "${lt_cv_aix_libpath+set}" = set; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath__CXX+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX="/usr/lib:/lib" fi fi aix_libpath=$lt_cv_aix_libpath__CXX fi hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib' allow_undefined_flag_CXX="-z nodefs" archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test "${lt_cv_aix_libpath+set}" = set; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath__CXX+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX="/usr/lib:/lib" fi fi aix_libpath=$lt_cv_aix_libpath__CXX fi hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag_CXX=' ${wl}-bernotok' allow_undefined_flag_CXX=' ${wl}-berok' if test "$with_gnu_ld" = yes; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec_CXX='$convenience' fi archive_cmds_need_lc_CXX=yes # This is similar to how AIX traditionally builds its shared # libraries. archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag_CXX=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else ld_shlibs_CXX=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl*) # Native MSVC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec_CXX=' ' allow_undefined_flag_CXX=unsupported always_export_symbols_CXX=yes file_list_spec_CXX='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; else $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true' enable_shared_with_static_runtimes_CXX=yes # Don't use ranlib old_postinstall_cmds_CXX='chmod 644 $oldlib' postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile="$lt_outputfile.exe" lt_tool_outputfile="$lt_tool_outputfile.exe" ;; esac~ func_to_tool_file "$lt_outputfile"~ if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec_CXX='-L$libdir' export_dynamic_flag_spec_CXX='${wl}--export-all-symbols' allow_undefined_flag_CXX=unsupported always_export_symbols_CXX=no enable_shared_with_static_runtimes_CXX=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs_CXX=no fi ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc_CXX=no hardcode_direct_CXX=no hardcode_automatic_CXX=yes hardcode_shlibpath_var_CXX=unsupported if test "$lt_cv_ld_force_load" = "yes"; then whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec_CXX='' fi link_all_deplibs_CXX=yes allow_undefined_flag_CXX="$_lt_dar_allow_undefined" case $cc_basename in ifort*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test "$_lt_dar_can_shared" = "yes"; then output_verbose_link_cmd=func_echo_all archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" if test "$lt_cv_apple_cc_single_mod" != "yes"; then archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" fi else ld_shlibs_CXX=no fi ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF ld_shlibs_CXX=no ;; freebsd-elf*) archive_cmds_need_lc_CXX=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions ld_shlibs_CXX=yes ;; gnu*) ;; haiku*) archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' link_all_deplibs_CXX=yes ;; hpux9*) hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' hardcode_libdir_separator_CXX=: export_dynamic_flag_spec_CXX='${wl}-E' hardcode_direct_CXX=yes hardcode_minus_L_CXX=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aCC*) archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test "$GXX" = yes; then archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; hpux10*|hpux11*) if test $with_gnu_ld = no; then hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' hardcode_libdir_separator_CXX=: case $host_cpu in hppa*64*|ia64*) ;; *) export_dynamic_flag_spec_CXX='${wl}-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) hardcode_direct_CXX=no hardcode_shlibpath_var_CXX=no ;; *) hardcode_direct_CXX=yes hardcode_direct_absolute_CXX=yes hardcode_minus_L_CXX=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aCC*) case $host_cpu in hppa*64*) archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test "$GXX" = yes; then if test $with_gnu_ld = no; then case $host_cpu in hppa*64*) archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; interix[3-9]*) hardcode_direct_CXX=no hardcode_shlibpath_var_CXX=no hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' export_dynamic_flag_spec_CXX='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds_CXX='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test "$GXX" = yes; then if test "$with_gnu_ld" = no; then archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' fi fi link_all_deplibs_CXX=yes ;; esac hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator_CXX=: inherit_rpath_CXX=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' export_dynamic_flag_spec_CXX='${wl}--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; esac archive_cmds_need_lc_CXX=no hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' export_dynamic_flag_spec_CXX='${wl}--export-dynamic' whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [1-5].* | *pgcpp\ [1-5].*) prelink_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' old_archive_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' archive_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' archive_expsym_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; esac hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir' export_dynamic_flag_spec_CXX='${wl}--export-dynamic' whole_archive_flag_spec_CXX='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' ;; cxx*) # Compaq C++ archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec_CXX='-rpath $libdir' hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' export_dynamic_flag_spec_CXX='${wl}--export-dynamic' archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 no_undefined_flag_CXX=' -zdefs' archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' archive_expsym_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' hardcode_libdir_flag_spec_CXX='-R$libdir' whole_archive_flag_spec_CXX='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' compiler_needs_object_CXX=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; m88k*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= hardcode_libdir_flag_spec_CXX='-R$libdir' hardcode_direct_CXX=yes hardcode_shlibpath_var_CXX=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) ld_shlibs_CXX=yes ;; openbsd2*) # C++ shared libraries are fairly broken ld_shlibs_CXX=no ;; openbsd*) if test -f /usr/libexec/ld.so; then hardcode_direct_CXX=yes hardcode_shlibpath_var_CXX=no hardcode_direct_absolute_CXX=yes archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' export_dynamic_flag_spec_CXX='${wl}-E' whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else ld_shlibs_CXX=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' hardcode_libdir_separator_CXX=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; cxx*) case $host in osf3*) allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' ;; *) allow_undefined_flag_CXX=' -expect_unresolved \*' archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ $RM $lib.exp' hardcode_libdir_flag_spec_CXX='-rpath $libdir' ;; esac hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test "$GXX" = yes && test "$with_gnu_ld" = no; then allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' case $host in osf3*) archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; *) archive_cmds_CXX='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; esac hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ archive_cmds_need_lc_CXX=yes no_undefined_flag_CXX=' -zdefs' archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' hardcode_libdir_flag_spec_CXX='-R$libdir' hardcode_shlibpath_var_CXX=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' ;; esac link_all_deplibs_CXX=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test "$GXX" = yes && test "$with_gnu_ld" = no; then no_undefined_flag_CXX=' ${wl}-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # g++ 2.7 appears to require `-G' NOT `-shared' on this # platform. archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' fi hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir' case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag_CXX='${wl}-z,text' archive_cmds_need_lc_CXX=no hardcode_shlibpath_var_CXX=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag_CXX='${wl}-z,text' allow_undefined_flag_CXX='${wl}-z,nodefs' archive_cmds_need_lc_CXX=no hardcode_shlibpath_var_CXX=no hardcode_libdir_flag_spec_CXX='${wl}-R,$libdir' hardcode_libdir_separator_CXX=':' link_all_deplibs_CXX=yes export_dynamic_flag_spec_CXX='${wl}-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ '"$old_archive_cmds_CXX" reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ '"$reload_cmds_CXX" ;; *) archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 $as_echo "$ld_shlibs_CXX" >&6; } test "$ld_shlibs_CXX" = no && can_build_shared=no GCC_CXX="$GXX" LD_CXX="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... # Dependencies to place before and after the object being linked: predep_objects_CXX= postdep_objects_CXX= predeps_CXX= postdeps_CXX= compiler_lib_search_path_CXX= cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case ${prev}${p} in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test $p = "-L" || test $p = "-R"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test "$pre_test_object_deps_done" = no; then case ${prev} in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$compiler_lib_search_path_CXX"; then compiler_lib_search_path_CXX="${prev}${p}" else compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${prev}${p}" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$postdeps_CXX"; then postdeps_CXX="${prev}${p}" else postdeps_CXX="${postdeps_CXX} ${prev}${p}" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test "$pre_test_object_deps_done" = no; then if test -z "$predep_objects_CXX"; then predep_objects_CXX="$p" else predep_objects_CXX="$predep_objects_CXX $p" fi else if test -z "$postdep_objects_CXX"; then postdep_objects_CXX="$p" else postdep_objects_CXX="$postdep_objects_CXX $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling CXX test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken case $host_os in interix[3-9]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. predep_objects_CXX= postdep_objects_CXX= postdeps_CXX= ;; linux*) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac if test "$solaris_use_stlport4" != yes; then postdeps_CXX='-library=Cstd -library=Crun' fi ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac # Adding this requires a known-good setup of shared libraries for # Sun compiler versions before 5.6, else PIC objects from an old # archive will be linked into the output, leading to subtle bugs. if test "$solaris_use_stlport4" != yes; then postdeps_CXX='-library=Cstd -library=Crun' fi ;; esac ;; esac case " $postdeps_CXX " in *" -lc "*) archive_cmds_need_lc_CXX=no ;; esac compiler_lib_search_dirs_CXX= if test -n "${compiler_lib_search_path_CXX}"; then compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` fi lt_prog_compiler_wl_CXX= lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX= # C++ specific cases for pic, static, wl, etc. if test "$GXX" = yes; then lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor lt_prog_compiler_static_CXX='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic_CXX='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic_CXX='-DDLL_EXPORT' ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic_CXX='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all lt_prog_compiler_pic_CXX= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static_CXX= ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic_CXX=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) lt_prog_compiler_pic_CXX='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic_CXX='-fPIC -shared' ;; *) lt_prog_compiler_pic_CXX='-fPIC' ;; esac else case $host_os in aix[4-9]*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor lt_prog_compiler_static_CXX='-Bstatic' else lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic_CXX='-DDLL_EXPORT' ;; dgux*) case $cc_basename in ec++*) lt_prog_compiler_pic_CXX='-KPIC' ;; ghcx*) # Green Hills C++ Compiler lt_prog_compiler_pic_CXX='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' if test "$host_cpu" != ia64; then lt_prog_compiler_pic_CXX='+Z' fi ;; aCC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic_CXX='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in KCC*) # KAI C++ Compiler lt_prog_compiler_wl_CXX='--backend -Wl,' lt_prog_compiler_pic_CXX='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64 which still supported -KPIC. lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-fPIC' lt_prog_compiler_static_CXX='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-fpic' lt_prog_compiler_static_CXX='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX='-non_shared' ;; xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) # IBM XL 8.0, 9.0 on PPC and BlueGene lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-qpic' lt_prog_compiler_static_CXX='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' lt_prog_compiler_wl_CXX='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) lt_prog_compiler_pic_CXX='-W c,exportall' ;; *) ;; esac ;; netbsd*) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic_CXX='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) lt_prog_compiler_wl_CXX='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 lt_prog_compiler_pic_CXX='-pic' ;; cxx*) # Digital/Compaq C++ lt_prog_compiler_wl_CXX='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' lt_prog_compiler_wl_CXX='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler lt_prog_compiler_pic_CXX='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x lt_prog_compiler_pic_CXX='-pic' lt_prog_compiler_static_CXX='-Bstatic' ;; lcc*) # Lucid lt_prog_compiler_pic_CXX='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 lt_prog_compiler_pic_CXX='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) lt_prog_compiler_can_build_shared_CXX=no ;; esac fi case $host_os in # For platforms which do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic_CXX= ;; *) lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } if ${lt_cv_prog_compiler_pic_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5 $as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; } lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works_CXX=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works_CXX=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 $as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } if test x"$lt_cv_prog_compiler_pic_works_CXX" = xyes; then case $lt_prog_compiler_pic_CXX in "" | " "*) ;; *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; esac else lt_prog_compiler_pic_CXX= lt_prog_compiler_can_build_shared_CXX=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if ${lt_cv_prog_compiler_static_works_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works_CXX=no save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works_CXX=yes fi else lt_cv_prog_compiler_static_works_CXX=yes fi fi $RM -r conftest* LDFLAGS="$save_LDFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 $as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } if test x"$lt_cv_prog_compiler_static_works_CXX" = xyes; then : else lt_prog_compiler_static_CXX= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o_CXX=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o_CXX=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 $as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o_CXX=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o_CXX=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 $as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } hard_links="nottested" if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test "$hard_links" = no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' case $host_os in aix[4-9]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm # Also, AIX nm treats weak defined symbols like other global defined # symbols, whereas GNU nm marks them as "W". if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi ;; pw32*) export_symbols_cmds_CXX="$ltdll_cmds" ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl*) exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' ;; esac ;; *) export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 $as_echo "$ld_shlibs_CXX" >&6; } test "$ld_shlibs_CXX" = no && can_build_shared=no with_gnu_ld_CXX=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc_CXX" in x|xyes) # Assume -lc should be added archive_cmds_need_lc_CXX=yes if test "$enable_shared" = yes && test "$GCC" = yes; then case $archive_cmds_CXX in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then : $as_echo_n "(cached) " >&6 else $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl_CXX pic_flag=$lt_prog_compiler_pic_CXX compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag_CXX allow_undefined_flag_CXX= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc_CXX=no else lt_cv_archive_cmds_need_lc_CXX=yes fi allow_undefined_flag_CXX=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 $as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; } archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=".so" postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='${libname}${release}${shared_ext}$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test "$host_cpu" = ia64; then # AIX 5 supports IA64 library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line `#! .'. This would cause the generated library to # depend on `.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # AIX (on Power*) has no versioning support, so currently we can not hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. if test "$aix_use_runtimelinking" = yes; then # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' else # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='${libname}${release}.a $libname.a' soname_spec='${libname}${release}${shared_ext}$major' fi shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='${libname}${shared_ext}' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=".dll" need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' library_names_spec='${libname}.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec="$LIB" if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' soname_spec='${libname}${release}${major}$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=yes sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' if test "X$HPUX_IA64_MODE" = X32; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" fi sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test "$lt_cv_prog_gnu_ld" = yes; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if ${lt_cv_shlibpath_overrides_runpath+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Append ld.so.conf contents to the search path if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd*) version_type=sunos sys_lib_dlsearch_path_spec="/usr/lib" need_lib_prefix=no # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. case $host_os in openbsd3.3 | openbsd3.3.*) need_version=yes ;; *) need_version=no ;; esac library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then case $host_os in openbsd2.[89] | openbsd2.[89].*) shlibpath_overrides_runpath=no ;; *) shlibpath_overrides_runpath=yes ;; esac else shlibpath_overrides_runpath=yes fi ;; os2*) libname_spec='$name' shrext_cmds=".dll" need_lib_prefix=no library_names_spec='$libname${shared_ext} $libname.a' dynamic_linker='OS/2 ld.exe' shlibpath_var=LIBPATH ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test "$with_gnu_ld" = yes; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec ;then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' soname_spec='$libname${shared_ext}.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=freebsd-elf need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test "$with_gnu_ld" = yes; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test "$dynamic_linker" = no && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test "$GCC" = yes; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" fi if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action_CXX= if test -n "$hardcode_libdir_flag_spec_CXX" || test -n "$runpath_var_CXX" || test "X$hardcode_automatic_CXX" = "Xyes" ; then # We can hardcode non-existent directories. if test "$hardcode_direct_CXX" != no && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" != no && test "$hardcode_minus_L_CXX" != no; then # Linking always hardcodes the temporary library directory. hardcode_action_CXX=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action_CXX=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action_CXX=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 $as_echo "$hardcode_action_CXX" >&6; } if test "$hardcode_action_CXX" = relink || test "$inherit_rpath_CXX" = yes; then # Fast installation is not supported enable_fast_install=no elif test "$shlibpath_overrides_runpath" = yes || test "$enable_shared" = no; then # Fast installation is not necessary enable_fast_install=needless fi fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test "$_lt_caught_CXX_error" != yes ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ac_config_commands="$ac_config_commands libtool" # Only expand once: # ---------------------------------------- # Additional checking of compiler characteristics # ---------------------------------------- # Check Endianness. If Big Endian, this will define WORDS_BIGENDIAN # See also at end of this file, where we define INTEL_BYTE_ORDER # or MOTOROLA_BYTE_ORDER. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 $as_echo_n "checking whether byte ordering is bigendian... " >&6; } if ${ac_cv_c_bigendian+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes; then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main () { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_cxx_try_run "$LINENO"; then : ac_cv_c_bigendian=no else ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 $as_echo "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac # ---------------------------------------- # Check for programs we need # ---------------------------------------- # Check where all the following programs are and set # variables accordingly: # ---------------------------------------- # C++ related options # ---------------------------------------- ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiling with clang" >&5 $as_echo_n "checking if compiling with clang... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __clang__ not clang #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : CLANG=yes else CLANG=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CLANG" >&5 $as_echo "$CLANG" >&6; } OLD_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports C++11" >&5 $as_echo_n "checking whether compiler supports C++11... " >&6; } CXXFLAGS="$CXXFLAGS -std=c++11" snprintfworks=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if (__cplusplus < 201103L) #error C++ 11 is unsupported #endif _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } has_cpp11=yes else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } has_cpp11=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext for ac_func in snprintf do : ac_fn_cxx_check_func "$LINENO" "snprintf" "ac_cv_func_snprintf" if test "x$ac_cv_func_snprintf" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SNPRINTF 1 _ACEOF else snprintfworks=yes fi done CXXFLAGS="$OLD_CXXFLAGS" # ---------------------------------------- # Check for libraries # ---------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing sem_init" >&5 $as_echo_n "checking for library containing sem_init... " >&6; } if ${ac_cv_search_sem_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char sem_init (); int main () { return sem_init (); ; return 0; } _ACEOF for ac_lib in '' pthread rt; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_search_sem_init=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_sem_init+:} false; then : break fi done if ${ac_cv_search_sem_init+:} false; then : else ac_cv_search_sem_init=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_sem_init" >&5 $as_echo "$ac_cv_search_sem_init" >&6; } ac_res=$ac_cv_search_sem_init if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi # ---------------------------------------- # Checks for header files. # ---------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_cxx_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5 $as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } if ${ac_cv_header_time+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { if ((struct tm *) 0) return 0; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_header_time=yes else ac_cv_header_time=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5 $as_echo "$ac_cv_header_time" >&6; } if test $ac_cv_header_time = yes; then $as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5 $as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } if ${ac_cv_header_sys_wait_h+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) #endif #ifndef WIFEXITED # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif int main () { int s; wait (&s); s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_header_sys_wait_h=yes else ac_cv_header_sys_wait_h=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5 $as_echo "$ac_cv_header_sys_wait_h" >&6; } if test $ac_cv_header_sys_wait_h = yes; then $as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h fi for ac_header in sys/ipc.h sys/shm.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in limits.h malloc.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done # Enable use of system-defined bool type if available: { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5 $as_echo_n "checking for stdbool.h that conforms to C99... " >&6; } if ${ac_cv_header_stdbool_h+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #ifndef bool "error: bool is not defined" #endif #ifndef false "error: false is not defined" #endif #if false "error: false is not 0" #endif #ifndef true "error: true is not defined" #endif #if true != 1 "error: true is not 1" #endif #ifndef __bool_true_false_are_defined "error: __bool_true_false_are_defined is not defined" #endif struct s { _Bool s: 1; _Bool t; } s; char a[true == 1 ? 1 : -1]; char b[false == 0 ? 1 : -1]; char c[__bool_true_false_are_defined == 1 ? 1 : -1]; char d[(bool) 0.5 == true ? 1 : -1]; /* See body of main program for 'e'. */ char f[(_Bool) 0.0 == false ? 1 : -1]; char g[true]; char h[sizeof (_Bool)]; char i[sizeof s.t]; enum { j = false, k = true, l = false * true, m = true * 256 }; /* The following fails for HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */ _Bool n[m]; char o[sizeof n == m * sizeof n[0] ? 1 : -1]; char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1]; /* Catch a bug in an HP-UX C compiler. See http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html */ _Bool q = true; _Bool *pq = &q; int main () { bool e = &s; *pq |= q; *pq |= ! q; /* Refer to every declared value, to avoid compiler optimizations. */ return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l + !m + !n + !o + !p + !q + !pq); ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_header_stdbool_h=yes else ac_cv_header_stdbool_h=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5 $as_echo "$ac_cv_header_stdbool_h" >&6; } ac_fn_cxx_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default" if test "x$ac_cv_type__Bool" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE__BOOL 1 _ACEOF fi if test $ac_cv_header_stdbool_h = yes; then $as_echo "#define HAVE_STDBOOL_H 1" >>confdefs.h fi # Misc { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether #! works in shell scripts" >&5 $as_echo_n "checking whether #! works in shell scripts... " >&6; } if ${ac_cv_sys_interpreter+:} false; then : $as_echo_n "(cached) " >&6 else echo '#! /bin/cat exit 69 ' >conftest chmod u+x conftest (SHELL=/bin/sh; export SHELL; ./conftest >/dev/null 2>&1) if test $? -ne 69; then ac_cv_sys_interpreter=yes else ac_cv_sys_interpreter=no fi rm -f conftest fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_interpreter" >&5 $as_echo "$ac_cv_sys_interpreter" >&6; } interpval=$ac_cv_sys_interpreter # Check whether --enable-largefile was given. if test "${enable_largefile+set}" = set; then : enableval=$enable_largefile; fi if test "$enable_largefile" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 $as_echo_n "checking for special C compiler options needed for large files... " >&6; } if ${ac_cv_sys_largefile_CC+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_sys_largefile_CC=no if test "$GCC" != yes; then ac_save_CC=$CC while :; do # IRIX 6.2 and later do not support large files by default, # so use the C compiler's -n32 option if that helps. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : break fi rm -f core conftest.err conftest.$ac_objext CC="$CC -n32" if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_sys_largefile_CC=' -n32'; break fi rm -f core conftest.err conftest.$ac_objext break done CC=$ac_save_CC rm -f conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 $as_echo "$ac_cv_sys_largefile_CC" >&6; } if test "$ac_cv_sys_largefile_CC" != no; then CC=$CC$ac_cv_sys_largefile_CC fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 $as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } if ${ac_cv_sys_file_offset_bits+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _FILE_OFFSET_BITS 64 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=64; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_file_offset_bits=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 $as_echo "$ac_cv_sys_file_offset_bits" >&6; } case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits _ACEOF ;; esac rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 $as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } if ${ac_cv_sys_large_files+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_sys_large_files=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGE_FILES 1 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_sys_large_files=1; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_large_files=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 $as_echo "$ac_cv_sys_large_files" >&6; } case $ac_cv_sys_large_files in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _LARGE_FILES $ac_cv_sys_large_files _ACEOF ;; esac rm -rf conftest* fi fi for ac_func in getline do : ac_fn_cxx_check_func "$LINENO" "getline" "ac_cv_func_getline" if test "x$ac_cv_func_getline" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_GETLINE 1 _ACEOF fi done # ---------------------------------------- # Checks for typedefs, structures, and compiler characteristics. # ---------------------------------------- ac_fn_cxx_check_type "$LINENO" "wchar_t" "ac_cv_type_wchar_t" "#include \"wchar.h\" " if test "x$ac_cv_type_wchar_t" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_WCHAR_T 1 _ACEOF fi ac_fn_cxx_check_type "$LINENO" "long long int" "ac_cv_type_long_long_int" "$ac_includes_default" if test "x$ac_cv_type_long_long_int" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LONG_LONG_INT 1 _ACEOF fi ac_fn_cxx_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "#include \"sys/types.h\" " if test "x$ac_cv_type_off_t" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_OFF_T 1 _ACEOF fi ac_fn_cxx_check_type "$LINENO" "mbstate_t" "ac_cv_type_mbstate_t" "#include \"wchar.h\" " if test "x$ac_cv_type_mbstate_t" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_MBSTATE_T 1 _ACEOF fi # ---------------------------------------- # Test auxiliary packages # ---------------------------------------- # Check location of leptonica/liblept headers. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for leptonica" >&5 $as_echo_n "checking for leptonica... " >&6; } have_lept=no if test "$LIBLEPT_HEADERSDIR" = "" ; then LIBLEPT_HEADERSDIR="/usr/local/include /usr/include /opt/local/include/leptonica" fi for incd in $LIBLEPT_HEADERSDIR do for lept in . leptonica liblept do if test -r "$incd/$lept/allheaders.h" ; then CPPFLAGS="$CPPFLAGS -I$incd/$lept" have_lept=yes fi done done if test "$have_lept" = yes ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pixCreate in -llept" >&5 $as_echo_n "checking for pixCreate in -llept... " >&6; } if ${ac_cv_lib_lept_pixCreate+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llept $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pixCreate (); int main () { return pixCreate (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_lept_pixCreate=yes else ac_cv_lib_lept_pixCreate=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lept_pixCreate" >&5 $as_echo "$ac_cv_lib_lept_pixCreate" >&6; } if test "x$ac_cv_lib_lept_pixCreate" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBLEPT 1 _ACEOF LIBS="-llept $LIBS" else as_fn_error $? "leptonica library missing" "$LINENO" 5 fi else as_fn_error $? "leptonica not found" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking leptonica version >= 1.71" >&5 $as_echo_n "checking leptonica version >= 1.71... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include "allheaders.h" int main () { #if (LIBLEPT_MAJOR_VERSION >= 1) && (LIBLEPT_MINOR_VERSION >= 71) int i = 0; #else #error You need to upgrade your leptonica library! #endif ; return 0; } _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "leptonica 1.71 or higher is required See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.err conftest.i conftest.$ac_ext # Check location of icu headers have_icu=false for ac_header in unicode/uchar.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "unicode/uchar.h" "ac_cv_header_unicode_uchar_h" "$ac_includes_default" if test "x$ac_cv_header_unicode_uchar_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_UNICODE_UCHAR_H 1 _ACEOF have_icu=true else have_icu=false fi done if !($have_icu); then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Training tools WILL NOT be built because of missing icu library." >&5 $as_echo "$as_me: WARNING: Training tools WILL NOT be built because of missing icu library." >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Try to install libicu-devel package." >&5 $as_echo "$as_me: WARNING: Try to install libicu-devel package." >&2;} fi if $have_icu; then ENABLE_TRAINING_TRUE= ENABLE_TRAINING_FALSE='#' else ENABLE_TRAINING_TRUE='#' ENABLE_TRAINING_FALSE= fi # Check location of pango headers have_pango=false for ac_header in pango-1.0/pango/pango-features.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "pango-1.0/pango/pango-features.h" "ac_cv_header_pango_1_0_pango_pango_features_h" "$ac_includes_default" if test "x$ac_cv_header_pango_1_0_pango_pango_features_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_PANGO_1_0_PANGO_PANGO_FEATURES_H 1 _ACEOF have_pango=true else have_pango=false fi done if !($have_pango); then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Training tools WILL NOT be built because of missing pango library." >&5 $as_echo "$as_me: WARNING: Training tools WILL NOT be built because of missing pango library." >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Try to install libpango1.0-dev package." >&5 $as_echo "$as_me: WARNING: Try to install libpango1.0-dev package." >&2;} else CPPFLAGS="$CPPFLAGS $(pkg-config --cflags pango)" fi if $have_pango; then ENABLE_TRAINING_TRUE= ENABLE_TRAINING_FALSE='#' else ENABLE_TRAINING_TRUE='#' ENABLE_TRAINING_FALSE= fi # Check location of cairo headers have_cairo=false for ac_header in cairo/cairo-version.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "cairo/cairo-version.h" "ac_cv_header_cairo_cairo_version_h" "$ac_includes_default" if test "x$ac_cv_header_cairo_cairo_version_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_CAIRO_CAIRO_VERSION_H 1 _ACEOF have_cairo=true else have_cairo=false fi done if !($have_cairo); then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Training tools WILL NOT be built because of missing cairo library." >&5 $as_echo "$as_me: WARNING: Training tools WILL NOT be built because of missing cairo library." >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Try to install libcairo-dev?? package." >&5 $as_echo "$as_me: WARNING: Try to install libcairo-dev?? package." >&2;} else CPPFLAGS="$CPPFLAGS $(pkg-config --cflags cairo)" fi if $have_cairo; then ENABLE_TRAINING_TRUE= ENABLE_TRAINING_FALSE='#' else ENABLE_TRAINING_TRUE='#' ENABLE_TRAINING_FALSE= fi # set c++11 support based on platform/compiler if test "x$has_cpp11" = "xyes"; then case "${host_os}" in cygwin*) CXXFLAGS="$CXXFLAGS -std=gnu++11" ;; *-darwin* | *-macos10*) if test "x$CLANG" = "xyes"; then CXXFLAGS="$CXXFLAGS -std=c++11 " LDFLAGS="$LDFLAGS -stdlib=libc++" else CXXFLAGS="$CXXFLAGS -std=c++11" fi ;; *) # default CXXFLAGS="$CXXFLAGS -std=c++11" ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Training tools WILL NOT be built because of missing c++11 support." >&5 $as_echo "$as_me: WARNING: Training tools WILL NOT be built because of missing c++11 support." >&2;} if test "x$has_cpp11" = "xyes"; then ENABLE_TRAINING_TRUE= ENABLE_TRAINING_FALSE='#' else ENABLE_TRAINING_TRUE='#' ENABLE_TRAINING_FALSE= fi fi # ---------------------------------------- # Final Tasks and Output # ---------------------------------------- # Output files ac_config_files="$ac_config_files Makefile tesseract.pc" ac_config_files="$ac_config_files api/Makefile" ac_config_files="$ac_config_files ccmain/Makefile" ac_config_files="$ac_config_files opencl/Makefile" ac_config_files="$ac_config_files ccstruct/Makefile" ac_config_files="$ac_config_files ccutil/Makefile" ac_config_files="$ac_config_files classify/Makefile" ac_config_files="$ac_config_files cube/Makefile" ac_config_files="$ac_config_files cutil/Makefile" ac_config_files="$ac_config_files dict/Makefile" ac_config_files="$ac_config_files neural_networks/runtime/Makefile" ac_config_files="$ac_config_files textord/Makefile" ac_config_files="$ac_config_files viewer/Makefile" ac_config_files="$ac_config_files wordrec/Makefile" ac_config_files="$ac_config_files tessdata/Makefile" ac_config_files="$ac_config_files tessdata/configs/Makefile" ac_config_files="$ac_config_files tessdata/tessconfigs/Makefile" ac_config_files="$ac_config_files testing/Makefile" ac_config_files="$ac_config_files java/Makefile" ac_config_files="$ac_config_files java/com/Makefile" ac_config_files="$ac_config_files java/com/google/Makefile" ac_config_files="$ac_config_files java/com/google/scrollview/Makefile" ac_config_files="$ac_config_files java/com/google/scrollview/events/Makefile" ac_config_files="$ac_config_files java/com/google/scrollview/ui/Makefile" ac_config_files="$ac_config_files doc/Makefile" if test -z "$ENABLE_TRAINING_TRUE"; then : ac_config_files="$ac_config_files training/Makefile" fi cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs { $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 $as_echo_n "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 $as_echo "done" >&6; } if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${T_WIN_TRUE}" && test -z "${T_WIN_FALSE}"; then as_fn_error $? "conditional \"T_WIN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${MINGW_TRUE}" && test -z "${MINGW_FALSE}"; then as_fn_error $? "conditional \"MINGW\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${OSX_TRUE}" && test -z "${OSX_FALSE}"; then as_fn_error $? "conditional \"OSX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${GRAPHICS_DISABLED_TRUE}" && test -z "${GRAPHICS_DISABLED_FALSE}"; then as_fn_error $? "conditional \"GRAPHICS_DISABLED\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${T_WIN_TRUE}" && test -z "${T_WIN_FALSE}"; then as_fn_error $? "conditional \"T_WIN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${MINGW_TRUE}" && test -z "${MINGW_FALSE}"; then as_fn_error $? "conditional \"MINGW\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ADD_RT_TRUE}" && test -z "${ADD_RT_FALSE}"; then as_fn_error $? "conditional \"ADD_RT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ADD_RT_TRUE}" && test -z "${ADD_RT_FALSE}"; then as_fn_error $? "conditional \"ADD_RT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${T_WIN_TRUE}" && test -z "${T_WIN_FALSE}"; then as_fn_error $? "conditional \"T_WIN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ADD_RT_TRUE}" && test -z "${ADD_RT_FALSE}"; then as_fn_error $? "conditional \"ADD_RT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ADD_RT_TRUE}" && test -z "${ADD_RT_FALSE}"; then as_fn_error $? "conditional \"ADD_RT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ADD_RT_TRUE}" && test -z "${ADD_RT_FALSE}"; then as_fn_error $? "conditional \"ADD_RT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${GRAPHICS_DISABLED_TRUE}" && test -z "${GRAPHICS_DISABLED_FALSE}"; then as_fn_error $? "conditional \"GRAPHICS_DISABLED\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${NO_CUBE_BUILD_TRUE}" && test -z "${NO_CUBE_BUILD_FALSE}"; then as_fn_error $? "conditional \"NO_CUBE_BUILD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${EMBEDDED_TRUE}" && test -z "${EMBEDDED_FALSE}"; then as_fn_error $? "conditional \"EMBEDDED\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${OPENMP_TRUE}" && test -z "${OPENMP_FALSE}"; then as_fn_error $? "conditional \"OPENMP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${OPENMP_TRUE}" && test -z "${OPENMP_FALSE}"; then as_fn_error $? "conditional \"OPENMP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${USE_OPENCL_TRUE}" && test -z "${USE_OPENCL_FALSE}"; then as_fn_error $? "conditional \"USE_OPENCL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${VISIBILITY_TRUE}" && test -z "${VISIBILITY_FALSE}"; then as_fn_error $? "conditional \"VISIBILITY\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${USING_MULTIPLELIBS_TRUE}" && test -z "${USING_MULTIPLELIBS_FALSE}"; then as_fn_error $? "conditional \"USING_MULTIPLELIBS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${NO_TESSDATA_PREFIX_TRUE}" && test -z "${NO_TESSDATA_PREFIX_FALSE}"; then as_fn_error $? "conditional \"NO_TESSDATA_PREFIX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_TRAINING_TRUE}" && test -z "${ENABLE_TRAINING_FALSE}"; then as_fn_error $? "conditional \"ENABLE_TRAINING\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_TRAINING_TRUE}" && test -z "${ENABLE_TRAINING_FALSE}"; then as_fn_error $? "conditional \"ENABLE_TRAINING\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_TRAINING_TRUE}" && test -z "${ENABLE_TRAINING_FALSE}"; then as_fn_error $? "conditional \"ENABLE_TRAINING\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_TRAINING_TRUE}" && test -z "${ENABLE_TRAINING_FALSE}"; then as_fn_error $? "conditional \"ENABLE_TRAINING\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by tesseract $as_me 3.04.01, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Configuration commands: $config_commands Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ tesseract config.status 3.04.01 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' sys_lib_dlsearch_path_spec='`$ECHO "$sys_lib_dlsearch_path_spec" | $SED "$delay_single_quote_subst"`' hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`' file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } # Quote evaled strings. for var in SHELL \ ECHO \ PATH_SEPARATOR \ SED \ GREP \ EGREP \ FGREP \ LD \ NM \ LN_S \ lt_SP2NL \ lt_NL2SP \ reload_flag \ OBJDUMP \ deplibs_check_method \ file_magic_cmd \ file_magic_glob \ want_nocaseglob \ DLLTOOL \ sharedlib_from_linklib_cmd \ AR \ AR_FLAGS \ archiver_list_spec \ STRIP \ RANLIB \ CC \ CFLAGS \ compiler \ lt_cv_sys_global_symbol_pipe \ lt_cv_sys_global_symbol_to_cdecl \ lt_cv_sys_global_symbol_to_c_name_address \ lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ nm_file_list_spec \ lt_prog_compiler_no_builtin_flag \ lt_prog_compiler_pic \ lt_prog_compiler_wl \ lt_prog_compiler_static \ lt_cv_prog_compiler_c_o \ need_locks \ MANIFEST_TOOL \ DSYMUTIL \ NMEDIT \ LIPO \ OTOOL \ OTOOL64 \ shrext_cmds \ export_dynamic_flag_spec \ whole_archive_flag_spec \ compiler_needs_object \ with_gnu_ld \ allow_undefined_flag \ no_undefined_flag \ hardcode_libdir_flag_spec \ hardcode_libdir_separator \ exclude_expsyms \ include_expsyms \ file_list_spec \ variables_saved_for_relink \ libname_spec \ library_names_spec \ soname_spec \ install_override_mode \ finish_eval \ old_striplib \ striplib \ compiler_lib_search_dirs \ predep_objects \ postdep_objects \ predeps \ postdeps \ compiler_lib_search_path \ LD_CXX \ reload_flag_CXX \ compiler_CXX \ lt_prog_compiler_no_builtin_flag_CXX \ lt_prog_compiler_pic_CXX \ lt_prog_compiler_wl_CXX \ lt_prog_compiler_static_CXX \ lt_cv_prog_compiler_c_o_CXX \ export_dynamic_flag_spec_CXX \ whole_archive_flag_spec_CXX \ compiler_needs_object_CXX \ with_gnu_ld_CXX \ allow_undefined_flag_CXX \ no_undefined_flag_CXX \ hardcode_libdir_flag_spec_CXX \ hardcode_libdir_separator_CXX \ exclude_expsyms_CXX \ include_expsyms_CXX \ file_list_spec_CXX \ compiler_lib_search_dirs_CXX \ predep_objects_CXX \ postdep_objects_CXX \ predeps_CXX \ postdeps_CXX \ compiler_lib_search_path_CXX; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in reload_cmds \ old_postinstall_cmds \ old_postuninstall_cmds \ old_archive_cmds \ extract_expsyms_cmds \ old_archive_from_new_cmds \ old_archive_from_expsyms_cmds \ archive_cmds \ archive_expsym_cmds \ module_cmds \ module_expsym_cmds \ export_symbols_cmds \ prelink_cmds \ postlink_cmds \ postinstall_cmds \ postuninstall_cmds \ finish_cmds \ sys_lib_search_path_spec \ sys_lib_dlsearch_path_spec \ reload_cmds_CXX \ old_archive_cmds_CXX \ old_archive_from_new_cmds_CXX \ old_archive_from_expsyms_cmds_CXX \ archive_cmds_CXX \ archive_expsym_cmds_CXX \ module_cmds_CXX \ module_expsym_cmds_CXX \ export_symbols_cmds_CXX \ prelink_cmds_CXX \ postlink_cmds_CXX; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done ac_aux_dir='$ac_aux_dir' xsi_shell='$xsi_shell' lt_shell_append='$lt_shell_append' # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi PACKAGE='$PACKAGE' VERSION='$VERSION' TIMESTAMP='$TIMESTAMP' RM='$RM' ofile='$ofile' _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "config_auto.h") CONFIG_HEADERS="$CONFIG_HEADERS config_auto.h:config/config.h.in" ;; "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "tesseract.pc") CONFIG_FILES="$CONFIG_FILES tesseract.pc" ;; "api/Makefile") CONFIG_FILES="$CONFIG_FILES api/Makefile" ;; "ccmain/Makefile") CONFIG_FILES="$CONFIG_FILES ccmain/Makefile" ;; "opencl/Makefile") CONFIG_FILES="$CONFIG_FILES opencl/Makefile" ;; "ccstruct/Makefile") CONFIG_FILES="$CONFIG_FILES ccstruct/Makefile" ;; "ccutil/Makefile") CONFIG_FILES="$CONFIG_FILES ccutil/Makefile" ;; "classify/Makefile") CONFIG_FILES="$CONFIG_FILES classify/Makefile" ;; "cube/Makefile") CONFIG_FILES="$CONFIG_FILES cube/Makefile" ;; "cutil/Makefile") CONFIG_FILES="$CONFIG_FILES cutil/Makefile" ;; "dict/Makefile") CONFIG_FILES="$CONFIG_FILES dict/Makefile" ;; "neural_networks/runtime/Makefile") CONFIG_FILES="$CONFIG_FILES neural_networks/runtime/Makefile" ;; "textord/Makefile") CONFIG_FILES="$CONFIG_FILES textord/Makefile" ;; "viewer/Makefile") CONFIG_FILES="$CONFIG_FILES viewer/Makefile" ;; "wordrec/Makefile") CONFIG_FILES="$CONFIG_FILES wordrec/Makefile" ;; "tessdata/Makefile") CONFIG_FILES="$CONFIG_FILES tessdata/Makefile" ;; "tessdata/configs/Makefile") CONFIG_FILES="$CONFIG_FILES tessdata/configs/Makefile" ;; "tessdata/tessconfigs/Makefile") CONFIG_FILES="$CONFIG_FILES tessdata/tessconfigs/Makefile" ;; "testing/Makefile") CONFIG_FILES="$CONFIG_FILES testing/Makefile" ;; "java/Makefile") CONFIG_FILES="$CONFIG_FILES java/Makefile" ;; "java/com/Makefile") CONFIG_FILES="$CONFIG_FILES java/com/Makefile" ;; "java/com/google/Makefile") CONFIG_FILES="$CONFIG_FILES java/com/google/Makefile" ;; "java/com/google/scrollview/Makefile") CONFIG_FILES="$CONFIG_FILES java/com/google/scrollview/Makefile" ;; "java/com/google/scrollview/events/Makefile") CONFIG_FILES="$CONFIG_FILES java/com/google/scrollview/events/Makefile" ;; "java/com/google/scrollview/ui/Makefile") CONFIG_FILES="$CONFIG_FILES java/com/google/scrollview/ui/Makefile" ;; "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "training/Makefile") CONFIG_FILES="$CONFIG_FILES training/Makefile" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. _am_arg="$ac_file" _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || $as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$_am_arg" : 'X\(//\)[^/]' \| \ X"$_am_arg" : 'X\(//\)$' \| \ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'`/stamp-h$_am_stamp_count ;; :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 $as_echo "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named 'Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`$as_dirname -- "$mf" || $as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$mf" : 'X\(//\)[^/]' \| \ X"$mf" : 'X\(//\)$' \| \ X"$mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "$am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`$as_dirname -- "$file" || $as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$file" : 'X\(//\)[^/]' \| \ X"$file" : 'X\(//\)$' \| \ X"$file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir=$dirpart/$fdir; as_fn_mkdir_p # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ;; "libtool":C) # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi cfgfile="${ofile}T" trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. # Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION # Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Free Software # Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is part of GNU Libtool. # # GNU Libtool is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, or # obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # The names of the tagged configurations supported by this script. available_tags="CXX " # ### BEGIN LIBTOOL CONFIG # Which release of libtool.m4 was used? macro_version=$macro_version macro_revision=$macro_revision # Whether or not to build shared libraries. build_libtool_libs=$enable_shared # Whether or not to build static libraries. build_old_libs=$enable_static # What type of objects to build. pic_mode=$pic_mode # Whether or not to optimize for fast installation. fast_install=$enable_fast_install # Shell to use when invoking shell scripts. SHELL=$lt_SHELL # An echo program that protects backslashes. ECHO=$lt_ECHO # The PATH separator for the build system. PATH_SEPARATOR=$lt_PATH_SEPARATOR # The host system. host_alias=$host_alias host=$host host_os=$host_os # The build system. build_alias=$build_alias build=$build build_os=$build_os # A sed program that does not truncate output. SED=$lt_SED # Sed that helps us avoid accidentally triggering echo(1) options like -n. Xsed="\$SED -e 1s/^X//" # A grep program that handles long lines. GREP=$lt_GREP # An ERE matcher. EGREP=$lt_EGREP # A literal string matcher. FGREP=$lt_FGREP # A BSD- or MS-compatible name lister. NM=$lt_NM # Whether we need soft or hard links. LN_S=$lt_LN_S # What is the maximum length of a command? max_cmd_len=$max_cmd_len # Object file suffix (normally "o"). objext=$ac_objext # Executable file suffix (normally ""). exeext=$exeext # whether the shell understands "unset". lt_unset=$lt_unset # turn spaces into newlines. SP2NL=$lt_lt_SP2NL # turn newlines into spaces. NL2SP=$lt_lt_NL2SP # convert \$build file names to \$host format. to_host_file_cmd=$lt_cv_to_host_file_cmd # convert \$build files to toolchain format. to_tool_file_cmd=$lt_cv_to_tool_file_cmd # An object symbol dumper. OBJDUMP=$lt_OBJDUMP # Method to check whether dependent libraries are shared objects. deplibs_check_method=$lt_deplibs_check_method # Command to use when deplibs_check_method = "file_magic". file_magic_cmd=$lt_file_magic_cmd # How to find potential files when deplibs_check_method = "file_magic". file_magic_glob=$lt_file_magic_glob # Find potential files using nocaseglob when deplibs_check_method = "file_magic". want_nocaseglob=$lt_want_nocaseglob # DLL creation program. DLLTOOL=$lt_DLLTOOL # Command to associate shared and link libraries. sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd # The archiver. AR=$lt_AR # Flags to create an archive. AR_FLAGS=$lt_AR_FLAGS # How to feed a file listing to the archiver. archiver_list_spec=$lt_archiver_list_spec # A symbol stripping program. STRIP=$lt_STRIP # Commands used to install an old-style archive. RANLIB=$lt_RANLIB old_postinstall_cmds=$lt_old_postinstall_cmds old_postuninstall_cmds=$lt_old_postuninstall_cmds # Whether to use a lock for old archive extraction. lock_old_archive_extraction=$lock_old_archive_extraction # A C compiler. LTCC=$lt_CC # LTCC compiler flags. LTCFLAGS=$lt_CFLAGS # Take the output of nm and produce a listing of raw symbols and C names. global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe # Transform the output of nm in a proper C declaration. global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl # Transform the output of nm in a C name address pair. global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address # Transform the output of nm in a C name address pair when lib prefix is needed. global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix # Specify filename containing input files for \$NM. nm_file_list_spec=$lt_nm_file_list_spec # The root where to search for dependent libraries,and in which our libraries should be installed. lt_sysroot=$lt_sysroot # The name of the directory that contains temporary libtool files. objdir=$objdir # Used to examine libraries when file_magic_cmd begins with "file". MAGIC_CMD=$MAGIC_CMD # Must we lock files when doing compilation? need_locks=$lt_need_locks # Manifest tool. MANIFEST_TOOL=$lt_MANIFEST_TOOL # Tool to manipulate archived DWARF debug symbol files on Mac OS X. DSYMUTIL=$lt_DSYMUTIL # Tool to change global to local symbols on Mac OS X. NMEDIT=$lt_NMEDIT # Tool to manipulate fat objects and archives on Mac OS X. LIPO=$lt_LIPO # ldd/readelf like tool for Mach-O binaries on Mac OS X. OTOOL=$lt_OTOOL # ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. OTOOL64=$lt_OTOOL64 # Old archive suffix (normally "a"). libext=$libext # Shared library suffix (normally ".so"). shrext_cmds=$lt_shrext_cmds # The commands to extract the exported symbol list from a shared archive. extract_expsyms_cmds=$lt_extract_expsyms_cmds # Variables whose values should be saved in libtool wrapper scripts and # restored at link time. variables_saved_for_relink=$lt_variables_saved_for_relink # Do we need the "lib" prefix for modules? need_lib_prefix=$need_lib_prefix # Do we need a version for libraries? need_version=$need_version # Library versioning type. version_type=$version_type # Shared library runtime path variable. runpath_var=$runpath_var # Shared library path variable. shlibpath_var=$shlibpath_var # Is shlibpath searched before the hard-coded library search path? shlibpath_overrides_runpath=$shlibpath_overrides_runpath # Format of library name prefix. libname_spec=$lt_libname_spec # List of archive names. First name is the real one, the rest are links. # The last name is the one that the linker finds with -lNAME library_names_spec=$lt_library_names_spec # The coded name of the library, if different from the real name. soname_spec=$lt_soname_spec # Permission mode override for installation of shared libraries. install_override_mode=$lt_install_override_mode # Command to use after installation of a shared archive. postinstall_cmds=$lt_postinstall_cmds # Command to use after uninstallation of a shared archive. postuninstall_cmds=$lt_postuninstall_cmds # Commands used to finish a libtool library installation in a directory. finish_cmds=$lt_finish_cmds # As "finish_cmds", except a single script fragment to be evaled but # not shown. finish_eval=$lt_finish_eval # Whether we should hardcode library paths into libraries. hardcode_into_libs=$hardcode_into_libs # Compile-time system search path for libraries. sys_lib_search_path_spec=$lt_sys_lib_search_path_spec # Run-time system search path for libraries. sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec # Whether dlopen is supported. dlopen_support=$enable_dlopen # Whether dlopen of programs is supported. dlopen_self=$enable_dlopen_self # Whether dlopen of statically linked programs is supported. dlopen_self_static=$enable_dlopen_self_static # Commands to strip libraries. old_striplib=$lt_old_striplib striplib=$lt_striplib # The linker used to build libraries. LD=$lt_LD # How to create reloadable object files. reload_flag=$lt_reload_flag reload_cmds=$lt_reload_cmds # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds # A language specific compiler. CC=$lt_compiler # Is the compiler the GNU compiler? with_gcc=$GCC # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds archive_expsym_cmds=$lt_archive_expsym_cmds # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds module_expsym_cmds=$lt_module_expsym_cmds # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator # Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct # Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \${shlibpath_var} if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms # Symbols that must always be exported. include_expsyms=$lt_include_expsyms # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds # Specify filename containing input files. file_list_spec=$lt_file_list_spec # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action # The directories searched by this compiler when creating a shared library. compiler_lib_search_dirs=$lt_compiler_lib_search_dirs # Dependencies to place before and after the objects being linked to # create a shared library. predep_objects=$lt_predep_objects postdep_objects=$lt_postdep_objects predeps=$lt_predeps postdeps=$lt_postdeps # The library search path used internally by the compiler when linking # a shared library. compiler_lib_search_path=$lt_compiler_lib_search_path # ### END LIBTOOL CONFIG _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac ltmain="$ac_aux_dir/ltmain.sh" # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) if test x"$xsi_shell" = xyes; then sed -e '/^func_dirname ()$/,/^} # func_dirname /c\ func_dirname ()\ {\ \ case ${1} in\ \ */*) func_dirname_result="${1%/*}${2}" ;;\ \ * ) func_dirname_result="${3}" ;;\ \ esac\ } # Extended-shell func_dirname implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_basename ()$/,/^} # func_basename /c\ func_basename ()\ {\ \ func_basename_result="${1##*/}"\ } # Extended-shell func_basename implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_dirname_and_basename ()$/,/^} # func_dirname_and_basename /c\ func_dirname_and_basename ()\ {\ \ case ${1} in\ \ */*) func_dirname_result="${1%/*}${2}" ;;\ \ * ) func_dirname_result="${3}" ;;\ \ esac\ \ func_basename_result="${1##*/}"\ } # Extended-shell func_dirname_and_basename implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_stripname ()$/,/^} # func_stripname /c\ func_stripname ()\ {\ \ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\ \ # positional parameters, so assign one to ordinary parameter first.\ \ func_stripname_result=${3}\ \ func_stripname_result=${func_stripname_result#"${1}"}\ \ func_stripname_result=${func_stripname_result%"${2}"}\ } # Extended-shell func_stripname implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_split_long_opt ()$/,/^} # func_split_long_opt /c\ func_split_long_opt ()\ {\ \ func_split_long_opt_name=${1%%=*}\ \ func_split_long_opt_arg=${1#*=}\ } # Extended-shell func_split_long_opt implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_split_short_opt ()$/,/^} # func_split_short_opt /c\ func_split_short_opt ()\ {\ \ func_split_short_opt_arg=${1#??}\ \ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}\ } # Extended-shell func_split_short_opt implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_lo2o ()$/,/^} # func_lo2o /c\ func_lo2o ()\ {\ \ case ${1} in\ \ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;\ \ *) func_lo2o_result=${1} ;;\ \ esac\ } # Extended-shell func_lo2o implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_xform ()$/,/^} # func_xform /c\ func_xform ()\ {\ func_xform_result=${1%.*}.lo\ } # Extended-shell func_xform implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_arith ()$/,/^} # func_arith /c\ func_arith ()\ {\ func_arith_result=$(( $* ))\ } # Extended-shell func_arith implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_len ()$/,/^} # func_len /c\ func_len ()\ {\ func_len_result=${#1}\ } # Extended-shell func_len implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: fi if test x"$lt_shell_append" = xyes; then sed -e '/^func_append ()$/,/^} # func_append /c\ func_append ()\ {\ eval "${1}+=\\${2}"\ } # Extended-shell func_append implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_append_quoted ()$/,/^} # func_append_quoted /c\ func_append_quoted ()\ {\ \ func_quote_for_eval "${2}"\ \ eval "${1}+=\\\\ \\$func_quote_for_eval_result"\ } # Extended-shell func_append_quoted implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: # Save a `func_append' function call where possible by direct use of '+=' sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: else # Save a `func_append' function call even when '+=' is not available sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: fi if test x"$_lt_function_replace_fail" = x":"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to substitute extended shell functions in $ofile" >&5 $as_echo "$as_me: WARNING: Unable to substitute extended shell functions in $ofile" >&2;} fi mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" cat <<_LT_EOF >> "$ofile" # ### BEGIN LIBTOOL TAG CONFIG: CXX # The linker used to build libraries. LD=$lt_LD_CXX # How to create reloadable object files. reload_flag=$lt_reload_flag_CXX reload_cmds=$lt_reload_cmds_CXX # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds_CXX # A language specific compiler. CC=$lt_compiler_CXX # Is the compiler the GNU compiler? with_gcc=$GCC_CXX # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic_CXX # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl_CXX # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static_CXX # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc_CXX # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object_CXX # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds_CXX archive_expsym_cmds=$lt_archive_expsym_cmds_CXX # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds_CXX module_expsym_cmds=$lt_module_expsym_cmds_CXX # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld_CXX # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag_CXX # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag_CXX # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX # Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct_CXX # Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \${shlibpath_var} if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute_CXX # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L_CXX # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic_CXX # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath_CXX # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs_CXX # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols_CXX # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds_CXX # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms_CXX # Symbols that must always be exported. include_expsyms=$lt_include_expsyms_CXX # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds_CXX # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds_CXX # Specify filename containing input files. file_list_spec=$lt_file_list_spec_CXX # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action_CXX # The directories searched by this compiler when creating a shared library. compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX # Dependencies to place before and after the objects being linked to # create a shared library. predep_objects=$lt_predep_objects_CXX postdep_objects=$lt_postdep_objects_CXX predeps=$lt_predeps_CXX postdeps=$lt_postdeps_CXX # The library search path used internally by the compiler when linking # a shared library. compiler_lib_search_path=$lt_compiler_lib_search_path_CXX # ### END LIBTOOL TAG CONFIG: CXX _LT_EOF ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi # Final message echo "" echo "Configuration is done." echo "You can now build and install $PACKAGE_NAME by running:" echo "" echo "$ make" echo "$ sudo make install" # echo "$ sudo make install LANGS=\"eng ara deu\"" # echo " Or:" # echo "$ sudo make install-langs" if test -z "$ENABLE_TRAINING_TRUE"; then : echo "" echo "Training tools can be build and installed (after building of $PACKAGE_NAME) with:" echo "" echo "$ make training" echo "$ sudo make training-install" echo "" else echo "" echo "You can not build training tools because of missing dependency." echo "Check configure output for details." echo "" fi # ---------------------------------------- # CONFIG Template # ---------------------------------------- # Fence added in configuration file # Stuff added at bottom of file tesseract-3.04.01/configure.ac000066400000000000000000000423251266071204500161530ustar00rootroot00000000000000# -*-Shell-script-*- # # Copyright (c) Luc Vincent # ---------------------------------------- # Initialization # ---------------------------------------- AC_PREREQ(2.50) AC_INIT([tesseract], [3.04.01], [https://github.com/tesseract-ocr/tesseract/issues]) AC_PROG_CXX(g++ clang++) AC_LANG([C++]) AC_LANG_COMPILER_REQUIRE CXXFLAGS=${CXXFLAGS:-""} AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR(config) AC_CONFIG_SRCDIR(api/tesseractmain.cpp) AC_PREFIX_DEFAULT(/usr/local) # Define date of package, etc. Could be useful in auto-generated # documentation. PACKAGE_YEAR=2016 PACKAGE_DATE="02/16" abs_top_srcdir=`AS_DIRNAME([$0])` gitrev="`git --git-dir=${abs_top_srcdir}/.git --work-tree=${abs_top_srcdir} describe --always --tags`" if test -n "${gitrev}" ; then AC_REVISION("${gitrev}") AC_DEFINE_UNQUOTED(GIT_REV,"${gitrev}", [Define to be the git revision]) echo "Using git revision: ${gitrev}" fi AC_DEFINE_UNQUOTED(PACKAGE_NAME,["${PACKAGE_NAME}"],[Name of package]) AC_DEFINE_UNQUOTED(PACKAGE_VERSION,["${PACKAGE_VERSION}"],[Version number]) AC_DEFINE_UNQUOTED(PACKAGE_YEAR,"$PACKAGE_YEAR",[Official year for this release]) AC_DEFINE_UNQUOTED(PACKAGE_DATE,"$PACKAGE_DATE",[Official date of release]) AC_SUBST(PACKAGE_NAME) AC_SUBST(PACKAGE_VERSION) AC_SUBST(PACKAGE_YEAR) AC_SUBST(PACKAGE_DATE) GENERIC_LIBRARY_NAME=tesseract # Release versioning GENERIC_MAJOR_VERSION=3 GENERIC_MINOR_VERSION=4 GENERIC_MICRO_VERSION=1 # API version (often = GENERIC_MAJOR_VERSION.GENERIC_MINOR_VERSION) GENERIC_API_VERSION=$GENERIC_MAJOR_VERSION.$GENERIC_MINOR_VERSION GENERIC_LIBRARY_VERSION=$GENERIC_MAJOR_VERSION:$GENERIC_MINOR_VERSION AC_SUBST(GENERIC_API_VERSION) AC_SUBST(GENERIC_MAJOR_VERSION) AC_SUBST(GENERIC_LIBRARY_VERSION) PACKAGE=$GENERIC_LIBRARY_NAME AC_SUBST(GENERIC_LIBRARY_NAME) GENERIC_VERSION=$GENERIC_MAJOR_VERSION.$GENERIC_MINOR_VERSION.$GENERIC_MICRO_VERSION GENERIC_RELEASE=$GENERIC_MAJOR_VERSION.$GENERIC_MINOR_VERSION AC_SUBST(GENERIC_RELEASE) AC_SUBST(GENERIC_VERSION) # ---------------------------------------- # Automake configuration # ---------------------------------------- # Do not require README file (we use README.md) AM_INIT_AUTOMAKE([foreign]) AC_CONFIG_HEADERS(config_auto.h:config/config.h.in) AM_MAINTAINER_MODE # default conditional AM_CONDITIONAL(T_WIN, false) AM_CONDITIONAL(MINGW, false) AM_CONDITIONAL(OSX, false) AM_CONDITIONAL(GRAPHICS_DISABLED, false) OPENCL_INC="/opt/AMDAPP/include" OPENCL_LIBS="-lOpenCL" ############################# # # Platform specific setup # ############################# AC_CANONICAL_HOST case "${host_os}" in mingw32*) AC_DEFINE_UNQUOTED(MINGW,1,[This is a MinGW system]) AM_CONDITIONAL(T_WIN, true) AM_CONDITIONAL(MINGW, true) AM_CONDITIONAL(ADD_RT, false) AC_SUBST([AM_LDFLAGS], ['-Wl,-no-undefined -Wl,--as-needed']) ;; cygwin*) AM_CONDITIONAL(ADD_RT, false) AM_CONDITIONAL(T_WIN, true) AC_SUBST([AM_LDFLAGS], ['-Wl,-no-undefined -Wl,--as-needed']) ;; solaris*) LIBS="-lsocket -lnsl -lrt -lxnet" AM_CONDITIONAL(ADD_RT, true) ;; *darwin*) OPENCL_LIBS="" OPENCL_INC="" AM_CONDITIONAL(ADD_RT, false) ;; powerpc-*-darwin*) OPENCL_LIBS="" ;; *) # default AM_CONDITIONAL(ADD_RT, true) ;; esac includedir="${includedir}/tesseract" AC_ARG_WITH(extra-includes, AC_HELP_STRING([--with-extra-includes=DIR], [Define an additional directory for include files]), [ if test -d "$withval" ; then CFLAGS="$CFLAGS -I$withval" else AC_MSG_ERROR([Cannot stat directory $withval]) fi ] ) AC_ARG_WITH(extra-libraries, AC_HELP_STRING([--with-extra-libraries=DIR], [Define an additional directory for library files]), [ if test -d "$withval" ; then LDFLAGS="$LDFLAGS -L$withval" else AC_MSG_ERROR([Cannot stat directory $withval]) fi ] ) AC_MSG_CHECKING(--enable-graphics argument) AC_ARG_ENABLE([graphics], [AC_HELP_STRING([--enable-graphics],[enable graphics (ScrollView) (default)]) AC_HELP_STRING([--disable-graphics],[disable graphics (ScrollView)])], [enable_graphics=$enableval], [enable_graphics="yes"]) AC_MSG_RESULT($enable_graphics) if test "$enable_graphics" = "no"; then AC_DEFINE([GRAPHICS_DISABLED], [], [Disable graphics]) AM_CONDITIONAL(GRAPHICS_DISABLED, true) fi # Check if cube should be disabled AC_MSG_CHECKING(whether to disable cube) AC_ARG_ENABLE([cube], [AC_HELP_STRING([--disable-cube], [don't build cube support (experimental)])], [disable_cube="yes"], [disable_cube="no"]) AC_MSG_RESULT($disable_cube) AM_CONDITIONAL([NO_CUBE_BUILD], [test "$disable_cube" = "yes"]) if test "$disable_cube" = "yes"; then AC_SUBST([AM_CPPFLAGS], [-DNO_CUBE_BUILD]) fi # check whether to build embedded version AC_MSG_CHECKING(--enable-embedded argument) AC_ARG_ENABLE([embedded], [ --enable-embedded enable embedded build (default=no)], [enable_embedded=$enableval], [enable_embedded="no"]) AC_MSG_RESULT($enable_embedded) AM_CONDITIONAL([EMBEDDED], [test "$enable_embedded" = "yes"]) if test "$enable_embedded" = "yes"; then AC_SUBST([AM_CPPFLAGS], [-DEMBEDDED]) fi # check whether to build OpenMP support AM_CONDITIONAL(OPENMP, false) AC_OPENMP AS_IF([test "x$OPENMP_CFLAGS" != "x"], AM_CONDITIONAL([OPENMP], true) AC_SUBST(AM_CPPFLAGS,"$OPENMP_CXXFLAGS") AC_DEFINE([OPENMP], [], [Defined when compiled with OpenMP support]) ) # check whether to build opencl version AC_MSG_CHECKING(--enable-opencl argument) AC_ARG_ENABLE([opencl], [ --enable-opencl enable opencl build (default=no)], [enable_opencl=$enableval], [enable_opencl="no"]) AC_MSG_RESULT($enable_opencl) # check for opencl header have_opencl=false AC_CHECK_HEADERS(CL/cl.h, have_opencl=true, [ AC_CHECK_HEADERS(OpenCL/cl.h, have_opencl=true, have_opencl=false) ]) have_tiff=false AC_CHECK_HEADERS(tiffio.h, have_tiff=true, have_tiff=false) # https://lists.apple.com/archives/unix-porting/2009/Jan/msg00026.html m4_define([MY_CHECK_FRAMEWORK], [AC_CACHE_CHECK([if -framework $1 works],[my_cv_framework_$1], [save_LIBS="$LIBS" LIBS="$LIBS -framework $1" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [my_cv_framework_$1=yes], [my_cv_framework_$1=no]) LIBS="$save_LIBS" ]) if test "$my_cv_framework_$1"="yes"; then AC_DEFINE(AS_TR_CPP([HAVE_FRAMEWORK_$1]),1, [Define if you have the $1 framework]) AS_TR_CPP([FRAMEWORK_$1])="-framework $1" AC_SUBST(AS_TR_CPP([FRAMEWORK_$1])) fi] ) have_opencl_lib=false OPENCL_CPPFLAGS='' OPENCL_LDFLAGS='' case "${host_os}" in *darwin* | *-macos10*) echo "checking for OpenCL framework" MY_CHECK_FRAMEWORK([OpenCL]) if test $my_cv_framework_OpenCL = yes; then have_opencl_lib=true fi if test "$enable_opencl" = "yes"; then if !($have_opencl_lib); then AC_MSG_ERROR(Required OpenCL library not found!) fi AC_SUBST([AM_CPPFLAGS], [-DUSE_OPENCL]) OPENCL_CPPFLAGS="" OPENCL_LDFLAGS="-framework OpenCL" fi ;; *) # default AC_CHECK_LIB(OpenCL, clGetPlatformIDs, have_opencl_lib=true, have_opencl_lib=false) if test "$enable_opencl" = "yes"; then if !($have_opencl); then AC_MSG_ERROR(Required OpenCL headers not found!) fi if !($have_opencl_lib); then AC_MSG_ERROR(Required OpenCL library not found!) fi if !($have_tiff); then AC_MSG_ERROR(Required TIFF headers not found! Try to install libtiff-dev?? package.) fi AC_SUBST([AM_CPPFLAGS], [-DUSE_OPENCL]) OPENCL_CPPFLAGS="-I${OPENCL_INC}" OPENCL_LDFLAGS="-l${OPENCL_LIBS}" fi ;; esac AM_CONDITIONAL([USE_OPENCL], [test "$enable_opencl" = "yes"]) AC_SUBST(OPENCL_CPPFLAGS) AC_SUBST(OPENCL_LDFLAGS) # check whether to build tesseract with -fvisibility=hidden -fvisibility-inlines-hidden # http://gcc.gnu.org/wiki/Visibility # http://groups.google.com/group/tesseract-dev/browse_thread/thread/976645ae98189127 AC_MSG_CHECKING(--enable-visibility argument) AC_ARG_ENABLE([visibility], [AC_HELP_STRING([--enable-visibility],[enable experimental build with fvisibility (default=no)])], [enable_visibility=$enableval], [enable_visibility="no"]) AC_MSG_RESULT($enable_visibility) AM_CONDITIONAL([VISIBILITY], [test "$enable_visibility" = "yes"]) # check whether to build multiple libraries AC_MSG_CHECKING(--enable-multiple-libraries argument) AC_ARG_ENABLE([multiple-libraries], [AC_HELP_STRING([--enable-multiple-libraries],[enable multiple libraries (default=no)])], [enable_mlibs=$enableval], [enable_mlibs="no"]) AC_MSG_RESULT($enable_mlibs) AM_CONDITIONAL([USING_MULTIPLELIBS], [test "$enable_mlibs" = "yes"]) # Check if tessdata-prefix is disabled AC_MSG_CHECKING(whether to use tessdata-prefix) AC_ARG_ENABLE(tessdata-prefix, [AC_HELP_STRING([--disable-tessdata-prefix], [don't set TESSDATA-PREFIX during compile])], [tessdata_prefix="no"], [tessdata_prefix="yes"]) AC_MSG_RESULT($tessdata_prefix) AM_CONDITIONAL([NO_TESSDATA_PREFIX], [test "$tessdata_prefix" = "no"]) # Check whether enable debuging AC_MSG_CHECKING(whether to enable debugging) AC_ARG_ENABLE([debug], [AC_HELP_STRING([--enable-debug], [turn on debugging (default=no)])], [debug=$enableval], [debug="no"]) AC_MSG_RESULT($debug) if test x"$debug" = x"yes"; then AM_CXXFLAGS="$AM_CXXFLAGS -g -Wall -Wno-uninitialized -O0 -DDEBUG" AM_CPPFLAGS="$AM_CPPFLAGS -g -Wall -Wno-uninitialized -O0 -DDEBUG" else AM_CXXFLAGS="$AM_CXXFLAGS -O2 -DNDEBUG" AM_CPPFLAGS="$AM_CPPFLAGS -O2 -DNDEBUG" fi #localedir='${prefix}/share/locale' # Always look into a "gnu" directory. curwd=`pwd` if test -d $curwd/gnu/include ; then CPPFLAGS="$CPPFLAGS -I$curwd/gnu/include" fi if test -d $curwd/gnu/lib ; then LDFLAGS="$LDFLAGS -L$curwd/gnu/lib" fi # ---------------------------------------- # Check Compiler Characteristics and # configure automake. The two appear to # be intimately linked... # ---------------------------------------- AC_PROG_LIBTOOL # ---------------------------------------- # Additional checking of compiler characteristics # ---------------------------------------- # Check Endianness. If Big Endian, this will define WORDS_BIGENDIAN # See also at end of this file, where we define INTEL_BYTE_ORDER # or MOTOROLA_BYTE_ORDER. AC_C_BIGENDIAN # ---------------------------------------- # Check for programs we need # ---------------------------------------- # Check where all the following programs are and set # variables accordingly: LT_INIT # ---------------------------------------- # C++ related options # ---------------------------------------- AC_LANG_CPLUSPLUS AC_MSG_CHECKING([if compiling with clang]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([], [[ #ifndef __clang__ not clang #endif ]])], [CLANG=yes], [CLANG=no]) AC_MSG_RESULT([$CLANG]) dnl ******************** dnl turn on c++11 dnl ******************** OLD_CXXFLAGS=$CXXFLAGS AC_MSG_CHECKING(whether compiler supports C++11) CXXFLAGS="$CXXFLAGS -std=c++11" snprintfworks=no AC_COMPILE_IFELSE( [ AC_LANG_SOURCE([[ #if (__cplusplus < 201103L) #error C++ 11 is unsupported #endif ]]) ], [ AC_MSG_RESULT(yes) has_cpp11=yes ], [ AC_MSG_RESULT(no) has_cpp11=no ]) AC_CHECK_FUNCS(snprintf,, [snprintfworks=yes]) CXXFLAGS="$OLD_CXXFLAGS" # ---------------------------------------- # Check for libraries # ---------------------------------------- AC_SEARCH_LIBS(sem_init,pthread rt) # ---------------------------------------- # Checks for header files. # ---------------------------------------- AC_HEADER_STDC AC_HEADER_TIME AC_HEADER_SYS_WAIT AC_CHECK_HEADERS(sys/ipc.h sys/shm.h) AC_CHECK_HEADERS(limits.h malloc.h) # Enable use of system-defined bool type if available: AC_HEADER_STDBOOL # Misc AC_SYS_INTERPRETER AC_SYS_LARGEFILE AC_CHECK_FUNCS([getline]) # ---------------------------------------- # Checks for typedefs, structures, and compiler characteristics. # ---------------------------------------- AC_CHECK_TYPES(wchar_t,,,[#include "wchar.h"]) AC_CHECK_TYPES(long long int) AC_CHECK_TYPES(off_t,,,[#include "sys/types.h"]) AC_CHECK_TYPES(mbstate_t,,,[#include "wchar.h"]) # ---------------------------------------- # Test auxiliary packages # ---------------------------------------- # Check location of leptonica/liblept headers. AC_MSG_CHECKING(for leptonica) AC_ARG_VAR(LIBLEPT_HEADERSDIR,[Leptonica headers directory]) have_lept=no if test "$LIBLEPT_HEADERSDIR" = "" ; then LIBLEPT_HEADERSDIR="/usr/local/include /usr/include /opt/local/include/leptonica" fi for incd in $LIBLEPT_HEADERSDIR do for lept in . leptonica liblept do if test -r "$incd/$lept/allheaders.h" ; then CPPFLAGS="$CPPFLAGS -I$incd/$lept" have_lept=yes fi done done if test "$have_lept" = yes ; then AC_MSG_RESULT(yes) AC_CHECK_LIB(lept,pixCreate,[], AC_MSG_ERROR([leptonica library missing])) else AC_MSG_ERROR([leptonica not found]) fi AC_MSG_CHECKING([leptonica version >= 1.71]) AC_PREPROC_IFELSE( [AC_LANG_PROGRAM([#include "allheaders.h"], [#if (LIBLEPT_MAJOR_VERSION >= 1) && (LIBLEPT_MINOR_VERSION >= 71) int i = 0; #else #error You need to upgrade your leptonica library! #endif])], [AC_MSG_RESULT(yes)], [AC_MSG_FAILURE([leptonica 1.71 or higher is required])]) # Check location of icu headers have_icu=false AC_CHECK_HEADERS(unicode/uchar.h, have_icu=true, have_icu=false) if !($have_icu); then AC_MSG_WARN(Training tools WILL NOT be built because of missing icu library.) AC_MSG_WARN(Try to install libicu-devel package.) fi AM_CONDITIONAL(ENABLE_TRAINING, $have_icu) # Check location of pango headers have_pango=false AC_CHECK_HEADERS(pango-1.0/pango/pango-features.h, have_pango=true, have_pango=false) if !($have_pango); then AC_MSG_WARN(Training tools WILL NOT be built because of missing pango library.) AC_MSG_WARN(Try to install libpango1.0-dev package.) else CPPFLAGS="$CPPFLAGS $(pkg-config --cflags pango)" fi AM_CONDITIONAL(ENABLE_TRAINING, $have_pango) # Check location of cairo headers have_cairo=false AC_CHECK_HEADERS(cairo/cairo-version.h, have_cairo=true, have_cairo=false) if !($have_cairo); then AC_MSG_WARN(Training tools WILL NOT be built because of missing cairo library.) AC_MSG_WARN(Try to install libcairo-dev?? package.) else CPPFLAGS="$CPPFLAGS $(pkg-config --cflags cairo)" fi AM_CONDITIONAL(ENABLE_TRAINING, $have_cairo) # set c++11 support based on platform/compiler if test "x$has_cpp11" = "xyes"; then case "${host_os}" in cygwin*) CXXFLAGS="$CXXFLAGS -std=gnu++11" ;; *-darwin* | *-macos10*) if test "x$CLANG" = "xyes"; then CXXFLAGS="$CXXFLAGS -std=c++11 " LDFLAGS="$LDFLAGS -stdlib=libc++" else CXXFLAGS="$CXXFLAGS -std=c++11" fi ;; *) # default CXXFLAGS="$CXXFLAGS -std=c++11" ;; esac else AC_MSG_WARN(Training tools WILL NOT be built because of missing c++11 support.) AM_CONDITIONAL(ENABLE_TRAINING, [test "x$has_cpp11" = "xyes"]) fi # ---------------------------------------- # Final Tasks and Output # ---------------------------------------- # Output files AC_CONFIG_FILES([Makefile tesseract.pc]) AC_CONFIG_FILES(api/Makefile) AC_CONFIG_FILES(ccmain/Makefile) AC_CONFIG_FILES(opencl/Makefile) AC_CONFIG_FILES(ccstruct/Makefile) AC_CONFIG_FILES(ccutil/Makefile) AC_CONFIG_FILES(classify/Makefile) AC_CONFIG_FILES(cube/Makefile) AC_CONFIG_FILES(cutil/Makefile) AC_CONFIG_FILES(dict/Makefile) AC_CONFIG_FILES(neural_networks/runtime/Makefile) AC_CONFIG_FILES(textord/Makefile) AC_CONFIG_FILES(viewer/Makefile) AC_CONFIG_FILES(wordrec/Makefile) AC_CONFIG_FILES(tessdata/Makefile) AC_CONFIG_FILES(tessdata/configs/Makefile) AC_CONFIG_FILES(tessdata/tessconfigs/Makefile) AC_CONFIG_FILES(testing/Makefile) AC_CONFIG_FILES(java/Makefile) AC_CONFIG_FILES(java/com/Makefile) AC_CONFIG_FILES(java/com/google/Makefile) AC_CONFIG_FILES(java/com/google/scrollview/Makefile) AC_CONFIG_FILES(java/com/google/scrollview/events/Makefile) AC_CONFIG_FILES(java/com/google/scrollview/ui/Makefile) AC_CONFIG_FILES(doc/Makefile) AM_COND_IF([ENABLE_TRAINING], AC_CONFIG_FILES(training/Makefile)) AC_OUTPUT # Final message echo "" echo "Configuration is done." echo "You can now build and install $PACKAGE_NAME by running:" echo "" echo "$ make" echo "$ sudo make install" # echo "$ sudo make install LANGS=\"eng ara deu\"" # echo " Or:" # echo "$ sudo make install-langs" AM_COND_IF([ENABLE_TRAINING], echo "" echo "Training tools can be build and installed (after building of $PACKAGE_NAME) with:" echo "" echo "$ make training" echo "$ sudo make training-install" echo "" , echo "" echo "You can not build training tools because of missing dependency." echo "Check configure output for details." echo "" ) # ---------------------------------------- # CONFIG Template # ---------------------------------------- # Fence added in configuration file AH_TOP([ #ifndef CONFIG_AUTO_H #define CONFIG_AUTO_H /* config_auto.h: begin */ ]) # Stuff added at bottom of file AH_BOTTOM([ /* Miscellaneous defines */ #define AUTOCONF 1 /* Not used yet #ifndef NO_GETTEXT #define USING_GETTEXT #endif */ /* config_auto.h: end */ #endif ]) tesseract-3.04.01/contrib/000077500000000000000000000000001266071204500153175ustar00rootroot00000000000000tesseract-3.04.01/contrib/tesseract-c_api-demo.py000077500000000000000000000042101266071204500216610ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2012 Zdenko Podobný # Author: Zdenko Podobný # # 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. """ Simple python demo script of tesseract-ocr 3.02 c-api """ import os import sys import ctypes # Demo variables lang = "eng" filename = "../phototest.tif" libpath = "/usr/local/lib64/" libpath_w = "../vs2010/DLL_Release/" TESSDATA_PREFIX = os.environ.get('TESSDATA_PREFIX') if not TESSDATA_PREFIX: TESSDATA_PREFIX = "../" if sys.platform == "win32": libname = libpath_w + "libtesseract302.dll" libname_alt = "libtesseract302.dll" os.environ["PATH"] += os.pathsep + libpath_w else: libname = libpath + "libtesseract.so.3.0.2" libname_alt = "libtesseract.so.3" try: tesseract = ctypes.cdll.LoadLibrary(libname) except: try: tesseract = ctypes.cdll.LoadLibrary(libname_alt) except WindowsError, err: print("Trying to load '%s'..." % libname) print("Trying to load '%s'..." % libname_alt) print(err) exit(1) tesseract.TessVersion.restype = ctypes.c_char_p tesseract_version = tesseract.TessVersion()[:4] # We need to check library version because libtesseract.so.3 is symlink # and can point to other version than 3.02 if float(tesseract_version) < 3.02: print("Found tesseract-ocr library version %s." % tesseract_version) print("C-API is present only in version 3.02!") exit(2) api = tesseract.TessBaseAPICreate() rc = tesseract.TessBaseAPIInit3(api, TESSDATA_PREFIX, lang); if (rc): tesseract.TessBaseAPIDelete(api) print("Could not initialize tesseract.\n") exit(3) text_out = tesseract.TessBaseAPIProcessPages(api, filename, None , 0); result_text = ctypes.string_at(text_out) print result_text tesseract-3.04.01/contrib/tesseract.completion000066400000000000000000000014251266071204500214110ustar00rootroot00000000000000#-*- mode: shell-script;-*- # # bash completion support for tesseract # # Copyright (C) 2009 Neskie A. Manuel # Distributed under the Apache License, Version 2.0. # _tesseract_languages() { local TESSDATA="/usr/share/tesseract-ocr/tessdata/" local langs="$(ls $TESSDATA | grep traineddata | cut -d \. -f 1)" COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W "$langs" -- "$cur") ) } _tesseract() { local cur prev COMPREPLY=() cur="$2" prev="$3" case "$prev" in tesseract) COMPREPLY=($(compgen -f -X "!*.+(tif)" -- "$cur") ) ;; *.tif) COMPREPLY=($(compgen -W "$(basename $prev .tif)" ) ) ;; -l) _tesseract_languages ;; *) COMPREPLY=($(compgen -W "-l" ) ) ;; esac } complete -F _tesseract -o nospace tesseract tesseract-3.04.01/contrib/traineddata.txt000066400000000000000000000007351266071204500203450ustar00rootroot00000000000000bul Bulgarian cat Catalan ces Czech chi_sim Simplified Chinese chi_tra Traditional Chinese dan-frak Danish (Fraktur) dan Danish deu German ell Greek eng English fin Finnish fra French hun Hungarian ind Indonesian ita Italian jpn Japanese kor Korean lav Latvian lit Lithuanian nld Dutch nor Norwegian pol Polish por Portuguese ron Romanian rus Russian slk Slovakian slv Slovenian spa Spanish srp Serbian swe Swedish tgl Tagalog tha Thai tur Turkish ukr Ukrainian vie Vietnamese tesseract-3.04.01/cube/000077500000000000000000000000001266071204500145755ustar00rootroot00000000000000tesseract-3.04.01/cube/Makefile.am000066400000000000000000000045311266071204500166340ustar00rootroot00000000000000AM_CPPFLAGS += \ -DUSE_STD_NAMESPACE \ -I$(top_srcdir)/cutil -I$(top_srcdir)/ccutil \ -I$(top_srcdir)/ccstruct -I$(top_srcdir)/dict \ -I$(top_srcdir)/ccmain -I$(top_srcdir)/classify \ -I$(top_srcdir)/textord -I$(top_srcdir)/wordrec \ -I$(top_srcdir)/neural_networks/runtime \ -I$(top_srcdir)/viewer if VISIBILITY AM_CPPFLAGS += -DTESS_EXPORTS \ -fvisibility=hidden -fvisibility-inlines-hidden endif noinst_HEADERS = \ altlist.h beam_search.h bmp_8.h cached_file.h \ char_altlist.h char_bigrams.h char_samp.h char_samp_enum.h \ char_samp_set.h char_set.h classifier_base.h classifier_factory.h \ con_comp.h cube_const.h conv_net_classifier.h cube_line_object.h \ cube_line_segmenter.h cube_object.h cube_search_object.h \ cube_tuning_params.h cube_utils.h feature_base.h feature_bmp.h \ feature_chebyshev.h feature_hybrid.h hybrid_neural_net_classifier.h \ lang_mod_edge.h lang_model.h search_column.h search_node.h \ search_object.h string_32.h tess_lang_mod_edge.h tess_lang_model.h \ tuning_params.h word_altlist.h word_list_lang_model.h word_size_model.h \ word_unigrams.h if !USING_MULTIPLELIBS noinst_LTLIBRARIES = libtesseract_cube.la else lib_LTLIBRARIES = libtesseract_cube.la libtesseract_cube_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) libtesseract_cube_la_LIBADD = \ ../ccstruct/libtesseract_ccstruct.la \ ../ccutil/libtesseract_ccutil.la \ ../neural_networks/runtime/libtesseract_neural.la \ ../viewer/libtesseract_viewer.la \ ../wordrec/libtesseract_wordrec.la \ ../cutil/libtesseract_cutil.la \ ../classify/libtesseract_classify.la \ ../dict/libtesseract_dict.la endif libtesseract_cube_la_SOURCES = \ altlist.cpp beam_search.cpp bmp_8.cpp cached_file.cpp \ char_altlist.cpp char_bigrams.cpp char_samp.cpp char_samp_enum.cpp \ char_samp_set.cpp char_set.cpp classifier_factory.cpp \ con_comp.cpp conv_net_classifier.cpp cube_line_object.cpp \ cube_line_segmenter.cpp cube_object.cpp cube_search_object.cpp \ cube_tuning_params.cpp cube_utils.cpp feature_bmp.cpp \ feature_chebyshev.cpp feature_hybrid.cpp hybrid_neural_net_classifier.cpp \ search_column.cpp search_node.cpp \ tess_lang_mod_edge.cpp tess_lang_model.cpp \ word_altlist.cpp word_list_lang_model.cpp word_size_model.cpp \ word_unigrams.cpp tesseract-3.04.01/cube/Makefile.in000066400000000000000000000654371266071204500166610ustar00rootroot00000000000000# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @VISIBILITY_TRUE@am__append_1 = -DTESS_EXPORTS \ @VISIBILITY_TRUE@ -fvisibility=hidden -fvisibility-inlines-hidden subdir = cube DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/config/depcomp $(noinst_HEADERS) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config_auto.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) @USING_MULTIPLELIBS_TRUE@libtesseract_cube_la_DEPENDENCIES = \ @USING_MULTIPLELIBS_TRUE@ ../ccstruct/libtesseract_ccstruct.la \ @USING_MULTIPLELIBS_TRUE@ ../ccutil/libtesseract_ccutil.la \ @USING_MULTIPLELIBS_TRUE@ ../neural_networks/runtime/libtesseract_neural.la \ @USING_MULTIPLELIBS_TRUE@ ../viewer/libtesseract_viewer.la \ @USING_MULTIPLELIBS_TRUE@ ../wordrec/libtesseract_wordrec.la \ @USING_MULTIPLELIBS_TRUE@ ../cutil/libtesseract_cutil.la \ @USING_MULTIPLELIBS_TRUE@ ../classify/libtesseract_classify.la \ @USING_MULTIPLELIBS_TRUE@ ../dict/libtesseract_dict.la am_libtesseract_cube_la_OBJECTS = altlist.lo beam_search.lo bmp_8.lo \ cached_file.lo char_altlist.lo char_bigrams.lo char_samp.lo \ char_samp_enum.lo char_samp_set.lo char_set.lo \ classifier_factory.lo con_comp.lo conv_net_classifier.lo \ cube_line_object.lo cube_line_segmenter.lo cube_object.lo \ cube_search_object.lo cube_tuning_params.lo cube_utils.lo \ feature_bmp.lo feature_chebyshev.lo feature_hybrid.lo \ hybrid_neural_net_classifier.lo search_column.lo \ search_node.lo tess_lang_mod_edge.lo tess_lang_model.lo \ word_altlist.lo word_list_lang_model.lo word_size_model.lo \ word_unigrams.lo libtesseract_cube_la_OBJECTS = $(am_libtesseract_cube_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libtesseract_cube_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ $(AM_CXXFLAGS) $(CXXFLAGS) $(libtesseract_cube_la_LDFLAGS) \ $(LDFLAGS) -o $@ @USING_MULTIPLELIBS_FALSE@am_libtesseract_cube_la_rpath = @USING_MULTIPLELIBS_TRUE@am_libtesseract_cube_la_rpath = -rpath \ @USING_MULTIPLELIBS_TRUE@ $(libdir) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__depfiles_maybe = depfiles am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(libtesseract_cube_la_SOURCES) DIST_SOURCES = $(libtesseract_cube_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_CPPFLAGS = @AM_CPPFLAGS@ -DUSE_STD_NAMESPACE -I$(top_srcdir)/cutil \ -I$(top_srcdir)/ccutil -I$(top_srcdir)/ccstruct \ -I$(top_srcdir)/dict -I$(top_srcdir)/ccmain \ -I$(top_srcdir)/classify -I$(top_srcdir)/textord \ -I$(top_srcdir)/wordrec \ -I$(top_srcdir)/neural_networks/runtime -I$(top_srcdir)/viewer \ $(am__append_1) AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AM_LDFLAGS = @AM_LDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FRAMEWORK_OPENCL = @FRAMEWORK_OPENCL@ GENERIC_API_VERSION = @GENERIC_API_VERSION@ GENERIC_LIBRARY_NAME = @GENERIC_LIBRARY_NAME@ GENERIC_LIBRARY_VERSION = @GENERIC_LIBRARY_VERSION@ GENERIC_MAJOR_VERSION = @GENERIC_MAJOR_VERSION@ GENERIC_RELEASE = @GENERIC_RELEASE@ GENERIC_VERSION = @GENERIC_VERSION@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBLEPT_HEADERSDIR = @LIBLEPT_HEADERSDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCL_CPPFLAGS = @OPENCL_CPPFLAGS@ OPENCL_LDFLAGS = @OPENCL_LDFLAGS@ OPENMP_CXXFLAGS = @OPENMP_CXXFLAGS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_DATE = @PACKAGE_DATE@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_YEAR = @PACKAGE_YEAR@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_HEADERS = \ altlist.h beam_search.h bmp_8.h cached_file.h \ char_altlist.h char_bigrams.h char_samp.h char_samp_enum.h \ char_samp_set.h char_set.h classifier_base.h classifier_factory.h \ con_comp.h cube_const.h conv_net_classifier.h cube_line_object.h \ cube_line_segmenter.h cube_object.h cube_search_object.h \ cube_tuning_params.h cube_utils.h feature_base.h feature_bmp.h \ feature_chebyshev.h feature_hybrid.h hybrid_neural_net_classifier.h \ lang_mod_edge.h lang_model.h search_column.h search_node.h \ search_object.h string_32.h tess_lang_mod_edge.h tess_lang_model.h \ tuning_params.h word_altlist.h word_list_lang_model.h word_size_model.h \ word_unigrams.h @USING_MULTIPLELIBS_FALSE@noinst_LTLIBRARIES = libtesseract_cube.la @USING_MULTIPLELIBS_TRUE@lib_LTLIBRARIES = libtesseract_cube.la @USING_MULTIPLELIBS_TRUE@libtesseract_cube_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) @USING_MULTIPLELIBS_TRUE@libtesseract_cube_la_LIBADD = \ @USING_MULTIPLELIBS_TRUE@ ../ccstruct/libtesseract_ccstruct.la \ @USING_MULTIPLELIBS_TRUE@ ../ccutil/libtesseract_ccutil.la \ @USING_MULTIPLELIBS_TRUE@ ../neural_networks/runtime/libtesseract_neural.la \ @USING_MULTIPLELIBS_TRUE@ ../viewer/libtesseract_viewer.la \ @USING_MULTIPLELIBS_TRUE@ ../wordrec/libtesseract_wordrec.la \ @USING_MULTIPLELIBS_TRUE@ ../cutil/libtesseract_cutil.la \ @USING_MULTIPLELIBS_TRUE@ ../classify/libtesseract_classify.la \ @USING_MULTIPLELIBS_TRUE@ ../dict/libtesseract_dict.la libtesseract_cube_la_SOURCES = \ altlist.cpp beam_search.cpp bmp_8.cpp cached_file.cpp \ char_altlist.cpp char_bigrams.cpp char_samp.cpp char_samp_enum.cpp \ char_samp_set.cpp char_set.cpp classifier_factory.cpp \ con_comp.cpp conv_net_classifier.cpp cube_line_object.cpp \ cube_line_segmenter.cpp cube_object.cpp cube_search_object.cpp \ cube_tuning_params.cpp cube_utils.cpp feature_bmp.cpp \ feature_chebyshev.cpp feature_hybrid.cpp hybrid_neural_net_classifier.cpp \ search_column.cpp search_node.cpp \ tess_lang_mod_edge.cpp tess_lang_model.cpp \ word_altlist.cpp word_list_lang_model.cpp word_size_model.cpp \ word_unigrams.cpp all: all-am .SUFFIXES: .SUFFIXES: .cpp .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign cube/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign cube/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libtesseract_cube.la: $(libtesseract_cube_la_OBJECTS) $(libtesseract_cube_la_DEPENDENCIES) $(EXTRA_libtesseract_cube_la_DEPENDENCIES) $(AM_V_CXXLD)$(libtesseract_cube_la_LINK) $(am_libtesseract_cube_la_rpath) $(libtesseract_cube_la_OBJECTS) $(libtesseract_cube_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/altlist.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/beam_search.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bmp_8.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cached_file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/char_altlist.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/char_bigrams.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/char_samp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/char_samp_enum.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/char_samp_set.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/char_set.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/classifier_factory.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/con_comp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conv_net_classifier.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cube_line_object.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cube_line_segmenter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cube_object.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cube_search_object.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cube_tuning_params.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cube_utils.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/feature_bmp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/feature_chebyshev.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/feature_hybrid.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hybrid_neural_net_classifier.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search_column.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search_node.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tess_lang_mod_edge.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tess_lang_model.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/word_altlist.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/word_list_lang_model.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/word_size_model.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/word_unigrams.Plo@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ clean-noinstLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-libLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-libLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libLTLIBRARIES clean-libtool clean-noinstLTLIBRARIES \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-libLTLIBRARIES \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-libLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: tesseract-3.04.01/cube/altlist.cpp000066400000000000000000000031751266071204500167630ustar00rootroot00000000000000/********************************************************************** * File: alt_list.cpp * Description: Class to abstarct a list of alternate results * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 "altlist.h" #include namespace tesseract { AltList::AltList(int max_alt) { max_alt_ = max_alt; alt_cnt_ = 0; alt_cost_ = NULL; alt_tag_ = NULL; } AltList::~AltList() { if (alt_cost_ != NULL) { delete []alt_cost_; alt_cost_ = NULL; } if (alt_tag_ != NULL) { delete []alt_tag_; alt_tag_ = NULL; } } // return the best possible cost and index of corresponding alternate int AltList::BestCost(int *best_alt) const { if (alt_cnt_ <= 0) { (*best_alt) = -1; return -1; } int best_alt_idx = 0; for (int alt_idx = 1; alt_idx < alt_cnt_; alt_idx++) { if (alt_cost_[alt_idx] < alt_cost_[best_alt_idx]) { best_alt_idx = alt_idx; } } (*best_alt) = best_alt_idx; return alt_cost_[best_alt_idx]; } } tesseract-3.04.01/cube/altlist.h000066400000000000000000000040521266071204500164230ustar00rootroot00000000000000/********************************************************************** * File: alt_list.h * Description: Class to abstarct a list of alternate results * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The AltList class is the base class for the list of alternate recognition // results. Each alternate has a cost an an optional tag associated with it #ifndef ALT_LIST_H #define ALT_LIST_H #include #include "cube_utils.h" namespace tesseract { class AltList { public: explicit AltList(int max_alt); virtual ~AltList(); // sort the list of alternates based virtual void Sort() = 0; // return the best possible cost and index of corresponding alternate int BestCost (int *best_alt) const; // return the count of alternates inline int AltCount() const { return alt_cnt_; } // returns the cost (-ve log prob) of an alternate inline int AltCost(int alt_idx) const { return alt_cost_[alt_idx]; } // returns the prob of an alternate inline double AltProb(int alt_idx) const { return CubeUtils::Cost2Prob(AltCost(alt_idx)); } // returns the alternate tag inline void *AltTag(int alt_idx) const { return alt_tag_[alt_idx]; } protected: // max number of alternates the list can hold int max_alt_; // actual alternate count int alt_cnt_; // array of alternate costs int *alt_cost_; // array of alternate tags void **alt_tag_; }; } #endif // ALT_LIST_H tesseract-3.04.01/cube/beam_search.cpp000066400000000000000000000377451266071204500175520ustar00rootroot00000000000000/********************************************************************** * File: beam_search.cpp * Description: Class to implement Beam Word Search Algorithm * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 "beam_search.h" #include "tesseractclass.h" namespace tesseract { BeamSearch::BeamSearch(CubeRecoContext *cntxt, bool word_mode) { cntxt_ = cntxt; seg_pt_cnt_ = 0; col_cnt_ = 1; col_ = NULL; word_mode_ = word_mode; } // Cleanup the lattice corresponding to the last search void BeamSearch::Cleanup() { if (col_ != NULL) { for (int col = 0; col < col_cnt_; col++) { if (col_[col]) delete col_[col]; } delete []col_; } col_ = NULL; } BeamSearch::~BeamSearch() { Cleanup(); } // Creates a set of children nodes emerging from a parent node based on // the character alternate list and the language model. void BeamSearch::CreateChildren(SearchColumn *out_col, LangModel *lang_mod, SearchNode *parent_node, LangModEdge *lm_parent_edge, CharAltList *char_alt_list, int extra_cost) { // get all the edges from this parent int edge_cnt; LangModEdge **lm_edges = lang_mod->GetEdges(char_alt_list, lm_parent_edge, &edge_cnt); if (lm_edges) { // add them to the ending column with the appropriate parent for (int edge = 0; edge < edge_cnt; edge++) { // add a node to the column if the current column is not the // last one, or if the lang model edge indicates it is valid EOW if (!cntxt_->NoisyInput() && out_col->ColIdx() >= seg_pt_cnt_ && !lm_edges[edge]->IsEOW()) { // free edge since no object is going to own it delete lm_edges[edge]; continue; } // compute the recognition cost of this node int recognition_cost = MIN_PROB_COST; if (char_alt_list && char_alt_list->AltCount() > 0) { recognition_cost = MAX(0, char_alt_list->ClassCost( lm_edges[edge]->ClassID())); // Add the no space cost. This should zero in word mode recognition_cost += extra_cost; } // Note that the edge will be freed inside the column if // AddNode is called if (recognition_cost >= 0) { out_col->AddNode(lm_edges[edge], recognition_cost, parent_node, cntxt_); } else { delete lm_edges[edge]; } } // edge // free edge array delete []lm_edges; } // lm_edges } // Performs a beam search in the specified search using the specified // language model; returns an alternate list of possible words as a result. WordAltList * BeamSearch::Search(SearchObject *srch_obj, LangModel *lang_mod) { // verifications if (!lang_mod) lang_mod = cntxt_->LangMod(); if (!lang_mod) { fprintf(stderr, "Cube ERROR (BeamSearch::Search): could not construct " "LangModel\n"); return NULL; } // free existing state Cleanup(); // get seg pt count seg_pt_cnt_ = srch_obj->SegPtCnt(); if (seg_pt_cnt_ < 0) { return NULL; } col_cnt_ = seg_pt_cnt_ + 1; // disregard suspicious cases if (seg_pt_cnt_ > 128) { fprintf(stderr, "Cube ERROR (BeamSearch::Search): segment point count is " "suspiciously high; bailing out\n"); return NULL; } // alloc memory for columns col_ = new SearchColumn *[col_cnt_]; if (!col_) { fprintf(stderr, "Cube ERROR (BeamSearch::Search): could not construct " "SearchColumn array\n"); return NULL; } memset(col_, 0, col_cnt_ * sizeof(*col_)); // for all possible segments for (int end_seg = 1; end_seg <= (seg_pt_cnt_ + 1); end_seg++) { // create a search column col_[end_seg - 1] = new SearchColumn(end_seg - 1, cntxt_->Params()->BeamWidth()); if (!col_[end_seg - 1]) { fprintf(stderr, "Cube ERROR (BeamSearch::Search): could not construct " "SearchColumn for column %d\n", end_seg - 1); return NULL; } // for all possible start segments int init_seg = MAX(0, end_seg - cntxt_->Params()->MaxSegPerChar()); for (int strt_seg = init_seg; strt_seg < end_seg; strt_seg++) { int parent_nodes_cnt; SearchNode **parent_nodes; // for the root segment, we do not have a parent if (strt_seg == 0) { parent_nodes_cnt = 1; parent_nodes = NULL; } else { // for all the existing nodes in the starting column parent_nodes_cnt = col_[strt_seg - 1]->NodeCount(); parent_nodes = col_[strt_seg - 1]->Nodes(); } // run the shape recognizer CharAltList *char_alt_list = srch_obj->RecognizeSegment(strt_seg - 1, end_seg - 1); // for all the possible parents for (int parent_idx = 0; parent_idx < parent_nodes_cnt; parent_idx++) { // point to the parent node SearchNode *parent_node = !parent_nodes ? NULL : parent_nodes[parent_idx]; LangModEdge *lm_parent_edge = !parent_node ? lang_mod->Root() : parent_node->LangModelEdge(); // compute the cost of not having spaces within the segment range int contig_cost = srch_obj->NoSpaceCost(strt_seg - 1, end_seg - 1); // In phrase mode, compute the cost of not having a space before // this character int no_space_cost = 0; if (!word_mode_ && strt_seg > 0) { no_space_cost = srch_obj->NoSpaceCost(strt_seg - 1); } // if the no space cost is low enough if ((contig_cost + no_space_cost) < MIN_PROB_COST) { // Add the children nodes CreateChildren(col_[end_seg - 1], lang_mod, parent_node, lm_parent_edge, char_alt_list, contig_cost + no_space_cost); } // In phrase mode and if not starting at the root if (!word_mode_ && strt_seg > 0) { // parent_node must be non-NULL // consider starting a new word for nodes that are valid EOW if (parent_node->LangModelEdge()->IsEOW()) { // get the space cost int space_cost = srch_obj->SpaceCost(strt_seg - 1); // if the space cost is low enough if ((contig_cost + space_cost) < MIN_PROB_COST) { // Restart the language model and add nodes as children to the // space node. CreateChildren(col_[end_seg - 1], lang_mod, parent_node, NULL, char_alt_list, contig_cost + space_cost); } } } } // parent } // strt_seg // prune the column nodes col_[end_seg - 1]->Prune(); // Free the column hash table. No longer needed col_[end_seg - 1]->FreeHashTable(); } // end_seg WordAltList *alt_list = CreateWordAltList(srch_obj); return alt_list; } // Creates a Word alternate list from the results in the lattice. WordAltList *BeamSearch::CreateWordAltList(SearchObject *srch_obj) { // create an alternate list of all the nodes in the last column int node_cnt = col_[col_cnt_ - 1]->NodeCount(); SearchNode **srch_nodes = col_[col_cnt_ - 1]->Nodes(); CharBigrams *bigrams = cntxt_->Bigrams(); WordUnigrams *word_unigrams = cntxt_->WordUnigramsObj(); // Save the index of the best-cost node before the alt list is // sorted, so that we can retrieve it from the node list when backtracking. best_presorted_node_idx_ = 0; int best_cost = -1; if (node_cnt <= 0) return NULL; // start creating the word alternate list WordAltList *alt_list = new WordAltList(node_cnt + 1); for (int node_idx = 0; node_idx < node_cnt; node_idx++) { // recognition cost int recognition_cost = srch_nodes[node_idx]->BestCost(); // compute the size cost of the alternate char_32 *ch_buff = NULL; int size_cost = SizeCost(srch_obj, srch_nodes[node_idx], &ch_buff); // accumulate other costs if (ch_buff) { int cost = 0; // char bigram cost int bigram_cost = !bigrams ? 0 : bigrams->Cost(ch_buff, cntxt_->CharacterSet()); // word unigram cost int unigram_cost = !word_unigrams ? 0 : word_unigrams->Cost(ch_buff, cntxt_->LangMod(), cntxt_->CharacterSet()); // overall cost cost = static_cast( (size_cost * cntxt_->Params()->SizeWgt()) + (bigram_cost * cntxt_->Params()->CharBigramWgt()) + (unigram_cost * cntxt_->Params()->WordUnigramWgt()) + (recognition_cost * cntxt_->Params()->RecoWgt())); // insert into word alt list alt_list->Insert(ch_buff, cost, static_cast(srch_nodes[node_idx])); // Note that strict < is necessary because WordAltList::Sort() // uses it in a bubble sort to swap entries. if (best_cost < 0 || cost < best_cost) { best_presorted_node_idx_ = node_idx; best_cost = cost; } delete []ch_buff; } } // sort the alternates based on cost alt_list->Sort(); return alt_list; } // Returns the lattice column corresponding to the specified column index. SearchColumn *BeamSearch::Column(int col) const { if (col < 0 || col >= col_cnt_ || !col_) return NULL; return col_[col]; } // Returns the best node in the last column of last performed search. SearchNode *BeamSearch::BestNode() const { if (col_cnt_ < 1 || !col_ || !col_[col_cnt_ - 1]) return NULL; int node_cnt = col_[col_cnt_ - 1]->NodeCount(); SearchNode **srch_nodes = col_[col_cnt_ - 1]->Nodes(); if (node_cnt < 1 || !srch_nodes || !srch_nodes[0]) return NULL; return srch_nodes[0]; } // Returns the string corresponding to the specified alt. char_32 *BeamSearch::Alt(int alt) const { // get the last column of the lattice if (col_cnt_ <= 0) return NULL; SearchColumn *srch_col = col_[col_cnt_ - 1]; if (!srch_col) return NULL; // point to the last node in the selected path if (alt >= srch_col->NodeCount() || srch_col->Nodes() == NULL) { return NULL; } SearchNode *srch_node = srch_col->Nodes()[alt]; if (!srch_node) return NULL; // get string char_32 *str32 = srch_node->PathString(); if (!str32) return NULL; return str32; } // Backtracks from the specified node index and returns the corresponding // character mapped segments and character count. Optional return // arguments are the char_32 result string and character bounding // boxes, if non-NULL values are passed in. CharSamp **BeamSearch::BackTrack(SearchObject *srch_obj, int node_index, int *char_cnt, char_32 **str32, Boxa **char_boxes) const { // get the last column of the lattice if (col_cnt_ <= 0) return NULL; SearchColumn *srch_col = col_[col_cnt_ - 1]; if (!srch_col) return NULL; // point to the last node in the selected path if (node_index >= srch_col->NodeCount() || !srch_col->Nodes()) return NULL; SearchNode *srch_node = srch_col->Nodes()[node_index]; if (!srch_node) return NULL; return BackTrack(srch_obj, srch_node, char_cnt, str32, char_boxes); } // Backtracks from the specified node index and returns the corresponding // character mapped segments and character count. Optional return // arguments are the char_32 result string and character bounding // boxes, if non-NULL values are passed in. CharSamp **BeamSearch::BackTrack(SearchObject *srch_obj, SearchNode *srch_node, int *char_cnt, char_32 **str32, Boxa **char_boxes) const { if (!srch_node) return NULL; if (str32) { if (*str32) delete [](*str32); // clear existing value *str32 = srch_node->PathString(); if (!*str32) return NULL; } if (char_boxes && *char_boxes) { boxaDestroy(char_boxes); // clear existing value } CharSamp **chars; chars = SplitByNode(srch_obj, srch_node, char_cnt, char_boxes); if (!chars && str32) delete []*str32; return chars; } // Backtracks from the given lattice node and return the corresponding // char mapped segments and character count. The character bounding // boxes are optional return arguments, if non-NULL values are passed in. CharSamp **BeamSearch::SplitByNode(SearchObject *srch_obj, SearchNode *srch_node, int *char_cnt, Boxa **char_boxes) const { // Count the characters (could be less than the path length when in // phrase mode) *char_cnt = 0; SearchNode *node = srch_node; while (node) { node = node->ParentNode(); (*char_cnt)++; } if (*char_cnt == 0) return NULL; // Allocate box array if (char_boxes) { if (*char_boxes) boxaDestroy(char_boxes); // clear existing value *char_boxes = boxaCreate(*char_cnt); if (*char_boxes == NULL) return NULL; } // Allocate memory for CharSamp array. CharSamp **chars = new CharSamp *[*char_cnt]; if (!chars) { if (char_boxes) boxaDestroy(char_boxes); return NULL; } int ch_idx = *char_cnt - 1; int seg_pt_cnt = srch_obj->SegPtCnt(); bool success=true; while (srch_node && ch_idx >= 0) { // Parent node (could be null) SearchNode *parent_node = srch_node->ParentNode(); // Get the seg pts corresponding to the search node int st_col = !parent_node ? 0 : parent_node->ColIdx() + 1; int st_seg_pt = st_col <= 0 ? -1 : st_col - 1; int end_col = srch_node->ColIdx(); int end_seg_pt = end_col >= seg_pt_cnt ? seg_pt_cnt : end_col; // Get a char sample corresponding to the segmentation points CharSamp *samp = srch_obj->CharSample(st_seg_pt, end_seg_pt); if (!samp) { success = false; break; } samp->SetLabel(srch_node->NodeString()); chars[ch_idx] = samp; if (char_boxes) { // Create the corresponding character bounding box Box *char_box = boxCreate(samp->Left(), samp->Top(), samp->Width(), samp->Height()); if (!char_box) { success = false; break; } boxaAddBox(*char_boxes, char_box, L_INSERT); } srch_node = parent_node; ch_idx--; } if (!success) { delete []chars; if (char_boxes) boxaDestroy(char_boxes); return NULL; } // Reverse the order of boxes. if (char_boxes) { int char_boxa_size = boxaGetCount(*char_boxes); int limit = char_boxa_size / 2; for (int i = 0; i < limit; ++i) { int box1_idx = i; int box2_idx = char_boxa_size - 1 - i; Box *box1 = boxaGetBox(*char_boxes, box1_idx, L_CLONE); Box *box2 = boxaGetBox(*char_boxes, box2_idx, L_CLONE); boxaReplaceBox(*char_boxes, box2_idx, box1); boxaReplaceBox(*char_boxes, box1_idx, box2); } } return chars; } // Returns the size cost of a string for a lattice path that // ends at the specified lattice node. int BeamSearch::SizeCost(SearchObject *srch_obj, SearchNode *node, char_32 **str32) const { CharSamp **chars = NULL; int char_cnt = 0; if (!node) return 0; // Backtrack to get string and character segmentation chars = BackTrack(srch_obj, node, &char_cnt, str32, NULL); if (!chars) return WORST_COST; int size_cost = (cntxt_->SizeModel() == NULL) ? 0 : cntxt_->SizeModel()->Cost(chars, char_cnt); delete []chars; return size_cost; } } // namespace tesesract tesseract-3.04.01/cube/beam_search.h000066400000000000000000000134211266071204500172000ustar00rootroot00000000000000/********************************************************************** * File: beam_search.h * Description: Declaration of Beam Word Search Algorithm Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The Beam Search class implements a Beam Search algorithm for the // N-best paths through the lattice of a search object using a language model // The search object is a segmented bitmap of a word image. The language model // is a state machine that defines valid sequences of characters // The cost of each path is the combined (product) probabilities of the // characters along the path. The character probabilities are computed using // the character classifier member of the RecoContext // The BeamSearch class itself holds the state of the last search it performed // using its "Search" method. Subsequent class to the Search method erase the // states of previously done searches #ifndef BEAM_SEARCH_H #define BEAM_SEARCH_H #include "search_column.h" #include "word_altlist.h" #include "search_object.h" #include "lang_model.h" #include "cube_utils.h" #include "cube_reco_context.h" #include "allheaders.h" namespace tesseract { class BeamSearch { public: explicit BeamSearch(CubeRecoContext *cntxt, bool word_mode = true); ~BeamSearch(); // Performs a beam search in the specified search using the specified // language model; returns an alternate list of possible words as a result. WordAltList *Search(SearchObject *srch_obj, LangModel *lang_mod = NULL); // Returns the best node in the last column of last performed search. SearchNode *BestNode() const; // Returns the string corresponding to the specified alt. char_32 *Alt(int alt) const; // Backtracks from the specified lattice node and returns the corresponding // character-mapped segments, character count, char_32 result string, and // character bounding boxes (if char_boxes is not NULL). If the segments // cannot be constructed, returns NULL, and all result arguments // will be NULL. CharSamp **BackTrack(SearchObject *srch_obj, int node_index, int *char_cnt, char_32 **str32, Boxa **char_boxes) const; // Same as above, except it takes a pointer to a search node object // instead of node index. CharSamp **BackTrack(SearchObject *srch_obj, SearchNode *node, int *char_cnt, char_32 **str32, Boxa **char_boxes) const; // Returns the size cost of a specified string of a lattice // path that ends at the specified lattice node. int SizeCost(SearchObject *srch_obj, SearchNode *node, char_32 **str32 = NULL) const; // Returns the word unigram cost of the given string, possibly // stripping out a single trailing punctuation character. int WordUnigramCost(char_32 *str32, WordUnigrams* word_unigrams) const; // Supplementary functions needed for visualization // Return column count of the lattice. inline int ColCnt() const { return col_cnt_; } // Returns the lattice column corresponding to the specified column index. SearchColumn *Column(int col_idx) const; // Return the index of the best node in the last column of the // best-cost path before the alternates list is sorted. inline int BestPresortedNodeIndex() const { return best_presorted_node_idx_; }; private: // Maximum reasonable segmentation point count static const int kMaxSegPointCnt = 128; // Recognition context object; the context holds the character classifier // and the tuning parameters object CubeRecoContext *cntxt_; // Count of segmentation pts int seg_pt_cnt_; // Lattice column count; currently redundant with respect to seg_pt_cnt_ // but that might change in the future int col_cnt_; // Array of lattice columns SearchColumn **col_; // Run in word or phrase mode bool word_mode_; // Node index of best-cost node, before alternates are merged and sorted int best_presorted_node_idx_; // Cleans up beam search state void Cleanup(); // Creates a Word alternate list from the results in the lattice. // This function computes a cost for each node in the final column // of the lattice, which is a weighted average of several costs: // size cost, character bigram cost, word unigram cost, and // recognition cost from the beam search. The weights are the // CubeTuningParams, which are learned together with the character // classifiers. WordAltList *CreateWordAltList(SearchObject *srch_obj); // Creates a set of children nodes emerging from a parent node based on // the character alternate list and the language model. void CreateChildren(SearchColumn *out_col, LangModel *lang_mod, SearchNode *parent_node, LangModEdge *lm_parent_edge, CharAltList *char_alt_list, int extra_cost); // Backtracks from the given lattice node and returns the corresponding // char mapped segments, character count, and character bounding boxes (if // char_boxes is not NULL). If the segments cannot be constructed, // returns NULL, and all result arguments will be NULL. CharSamp **SplitByNode(SearchObject *srch_obj, SearchNode *srch_node, int* char_cnt, Boxa **char_boxes) const; }; } #endif // BEAM_SEARCH_H tesseract-3.04.01/cube/bmp_8.cpp000066400000000000000000000671071266071204500163210ustar00rootroot00000000000000/********************************************************************** * File: bmp_8.cpp * Description: Implementation of an 8-bit Bitmap class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 #include #include #include "bmp_8.h" #include "con_comp.h" #include "platform.h" #ifdef USE_STD_NAMESPACE using std::min; using std::max; #endif namespace tesseract { const int Bmp8::kDeslantAngleCount = (1 + static_cast(0.5f + (kMaxDeslantAngle - kMinDeslantAngle) / kDeslantAngleDelta)); float *Bmp8::tan_table_ = NULL; Bmp8::Bmp8(unsigned short wid, unsigned short hgt) : wid_(wid) , hgt_(hgt) { line_buff_ = CreateBmpBuffer(); } Bmp8::~Bmp8() { FreeBmpBuffer(line_buff_); } // free buffer void Bmp8::FreeBmpBuffer(unsigned char **buff) { if (buff != NULL) { if (buff[0] != NULL) { delete []buff[0]; } delete []buff; } } void Bmp8::FreeBmpBuffer(unsigned int **buff) { if (buff != NULL) { if (buff[0] != NULL) { delete []buff[0]; } delete []buff; } } // init bmp buffers unsigned char **Bmp8::CreateBmpBuffer(unsigned char init_val) { unsigned char **buff; // Check valid sizes if (!hgt_ || !wid_) return NULL; // compute stride (align on 4 byte boundries) stride_ = ((wid_ % 4) == 0) ? wid_ : (4 * (1 + (wid_ / 4))); buff = (unsigned char **) new unsigned char *[hgt_ * sizeof(*buff)]; if (!buff) { delete []buff; return NULL; } // alloc and init memory for buffer and line buffer buff[0] = (unsigned char *) new unsigned char[stride_ * hgt_ * sizeof(*buff[0])]; if (!buff[0]) { return NULL; } memset(buff[0], init_val, stride_ * hgt_ * sizeof(*buff[0])); for (int y = 1; y < hgt_; y++) { buff[y] = buff[y -1] + stride_; } return buff; } // init bmp buffers unsigned int ** Bmp8::CreateBmpBuffer(int wid, int hgt, unsigned char init_val) { unsigned int **buff; // compute stride (align on 4 byte boundries) buff = (unsigned int **) new unsigned int *[hgt * sizeof(*buff)]; if (!buff) { delete []buff; return NULL; } // alloc and init memory for buffer and line buffer buff[0] = (unsigned int *) new unsigned int[wid * hgt * sizeof(*buff[0])]; if (!buff[0]) { return NULL; } memset(buff[0], init_val, wid * hgt * sizeof(*buff[0])); for (int y = 1; y < hgt; y++) { buff[y] = buff[y -1] + wid; } return buff; } // clears the contents of the bmp bool Bmp8::Clear() { if (line_buff_ == NULL) { return false; } memset(line_buff_[0], 0xff, stride_ * hgt_ * sizeof(*line_buff_[0])); return true; } bool Bmp8::LoadFromCharDumpFile(CachedFile *fp) { unsigned short wid; unsigned short hgt; unsigned short x; unsigned short y; int buf_size; int pix; int pix_cnt; unsigned int val32; unsigned char *buff; // read and check 32 bit marker if (fp->Read(&val32, sizeof(val32)) != sizeof(val32)) { return false; } if (val32 != kMagicNumber) { return false; } // read wid and hgt if (fp->Read(&wid, sizeof(wid)) != sizeof(wid)) { return false; } if (fp->Read(&hgt, sizeof(hgt)) != sizeof(hgt)) { return false; } // read buf size if (fp->Read(&buf_size, sizeof(buf_size)) != sizeof(buf_size)) { return false; } // validate buf size: for now, only 3 channel (RBG) is supported pix_cnt = wid * hgt; if (buf_size != (3 * pix_cnt)) { return false; } // alloc memory & read the 3 channel buffer buff = new unsigned char[buf_size]; if (buff == NULL) { return false; } if (fp->Read(buff, buf_size) != buf_size) { delete []buff; return false; } // create internal buffers wid_ = wid; hgt_ = hgt; line_buff_ = CreateBmpBuffer(); if (line_buff_ == NULL) { delete []buff; return false; } // copy the data for (y = 0, pix = 0; y < hgt_; y++) { for (x = 0; x < wid_; x++, pix += 3) { // for now we only support gray scale, // so we expect R = G = B, it this is not the case, bail out if (buff[pix] != buff[pix + 1] || buff[pix] != buff[pix + 2]) { delete []buff; return false; } line_buff_[y][x] = buff[pix]; } } // delete temp buffer delete[]buff; return true; } Bmp8 * Bmp8::FromCharDumpFile(CachedFile *fp) { // create a Bmp8 object Bmp8 *bmp_obj = new Bmp8(0, 0); if (bmp_obj == NULL) { return NULL; } if (bmp_obj->LoadFromCharDumpFile(fp) == false) { delete bmp_obj; return NULL; } return bmp_obj; } bool Bmp8::LoadFromCharDumpFile(FILE *fp) { unsigned short wid; unsigned short hgt; unsigned short x; unsigned short y; int buf_size; int pix; int pix_cnt; unsigned int val32; unsigned char *buff; // read and check 32 bit marker if (fread(&val32, 1, sizeof(val32), fp) != sizeof(val32)) { return false; } if (val32 != kMagicNumber) { return false; } // read wid and hgt if (fread(&wid, 1, sizeof(wid), fp) != sizeof(wid)) { return false; } if (fread(&hgt, 1, sizeof(hgt), fp) != sizeof(hgt)) { return false; } // read buf size if (fread(&buf_size, 1, sizeof(buf_size), fp) != sizeof(buf_size)) { return false; } // validate buf size: for now, only 3 channel (RBG) is supported pix_cnt = wid * hgt; if (buf_size != (3 * pix_cnt)) { return false; } // alloc memory & read the 3 channel buffer buff = new unsigned char[buf_size]; if (buff == NULL) { return false; } if (fread(buff, 1, buf_size, fp) != buf_size) { delete []buff; return false; } // create internal buffers wid_ = wid; hgt_ = hgt; line_buff_ = CreateBmpBuffer(); if (line_buff_ == NULL) { delete []buff; return false; } // copy the data for (y = 0, pix = 0; y < hgt_; y++) { for (x = 0; x < wid_; x++, pix += 3) { // for now we only support gray scale, // so we expect R = G = B, it this is not the case, bail out if (buff[pix] != buff[pix + 1] || buff[pix] != buff[pix + 2]) { delete []buff; return false; } line_buff_[y][x] = buff[pix]; } } // delete temp buffer delete[]buff; return true; } Bmp8 * Bmp8::FromCharDumpFile(FILE *fp) { // create a Bmp8 object Bmp8 *bmp_obj = new Bmp8(0, 0); if (bmp_obj == NULL) { return NULL; } if (bmp_obj->LoadFromCharDumpFile(fp) == false) { delete bmp_obj; return NULL; } return bmp_obj; } bool Bmp8::IsBlankColumn(int x) const { for (int y = 0; y < hgt_; y++) { if (line_buff_[y][x] != 0xff) { return false; } } return true; } bool Bmp8::IsBlankRow(int y) const { for (int x = 0; x < wid_; x++) { if (line_buff_[y][x] != 0xff) { return false; } } return true; } // crop the bitmap returning new dimensions void Bmp8::Crop(int *xst, int *yst, int *wid, int *hgt) { (*xst) = 0; (*yst) = 0; int xend = wid_ - 1; int yend = hgt_ - 1; while ((*xst) < (wid_ - 1) && (*xst) <= xend) { // column is not empty if (!IsBlankColumn((*xst))) { break; } (*xst)++; } while (xend > 0 && xend >= (*xst)) { // column is not empty if (!IsBlankColumn(xend)) { break; } xend--; } while ((*yst) < (hgt_ - 1) && (*yst) <= yend) { // column is not empty if (!IsBlankRow((*yst))) { break; } (*yst)++; } while (yend > 0 && yend >= (*yst)) { // column is not empty if (!IsBlankRow(yend)) { break; } yend--; } (*wid) = xend - (*xst) + 1; (*hgt) = yend - (*yst) + 1; } // generates a scaled bitmap with dimensions the new bmp will have the // same aspect ratio and will be centered in the box bool Bmp8::ScaleFrom(Bmp8 *bmp, bool isotropic) { int x_num; int x_denom; int y_num; int y_denom; int xoff; int yoff; int xsrc; int ysrc; int xdest; int ydest; int xst_src = 0; int yst_src = 0; int xend_src = bmp->wid_ - 1; int yend_src = bmp->hgt_ - 1; int wid_src; int hgt_src; // src dimensions wid_src = xend_src - xst_src + 1, hgt_src = yend_src - yst_src + 1; // scale to maintain aspect ratio if required if (isotropic) { if ((wid_ * hgt_src) > (hgt_ * wid_src)) { x_num = y_num = hgt_; x_denom = y_denom = hgt_src; } else { x_num = y_num = wid_; x_denom = y_denom = wid_src; } } else { x_num = wid_; y_num = hgt_; x_denom = wid_src; y_denom = hgt_src; } // compute offsets needed to center new bmp xoff = (wid_ - ((x_num * wid_src) / x_denom)) / 2; yoff = (hgt_ - ((y_num * hgt_src) / y_denom)) / 2; // scale up if (y_num > y_denom) { for (ydest = yoff; ydest < (hgt_ - yoff); ydest++) { // compute un-scaled y ysrc = static_cast(0.5 + (1.0 * (ydest - yoff) * y_denom / y_num)); if (ysrc < 0 || ysrc >= hgt_src) { continue; } for (xdest = xoff; xdest < (wid_ - xoff); xdest++) { // compute un-scaled y xsrc = static_cast(0.5 + (1.0 * (xdest - xoff) * x_denom / x_num)); if (xsrc < 0 || xsrc >= wid_src) { continue; } line_buff_[ydest][xdest] = bmp->line_buff_[ysrc + yst_src][xsrc + xst_src]; } } } else { // or scale down // scaling down is a bit tricky: we'll accumulate pixels // and then compute the means unsigned int **dest_line_buff = CreateBmpBuffer(wid_, hgt_, 0), **dest_pix_cnt = CreateBmpBuffer(wid_, hgt_, 0); for (ysrc = 0; ysrc < hgt_src; ysrc++) { // compute scaled y ydest = yoff + static_cast(0.5 + (1.0 * ysrc * y_num / y_denom)); if (ydest < 0 || ydest >= hgt_) { continue; } for (xsrc = 0; xsrc < wid_src; xsrc++) { // compute scaled y xdest = xoff + static_cast(0.5 + (1.0 * xsrc * x_num / x_denom)); if (xdest < 0 || xdest >= wid_) { continue; } dest_line_buff[ydest][xdest] += bmp->line_buff_[ysrc + yst_src][xsrc + xst_src]; dest_pix_cnt[ydest][xdest]++; } } for (ydest = 0; ydest < hgt_; ydest++) { for (xdest = 0; xdest < wid_; xdest++) { if (dest_pix_cnt[ydest][xdest] > 0) { unsigned int pixval = dest_line_buff[ydest][xdest] / dest_pix_cnt[ydest][xdest]; line_buff_[ydest][xdest] = (unsigned char) min((unsigned int)255, pixval); } } } // we no longer need these temp buffers FreeBmpBuffer(dest_line_buff); FreeBmpBuffer(dest_pix_cnt); } return true; } bool Bmp8::LoadFromRawData(unsigned char *data) { unsigned char *pline_data = data; // copy the data for (int y = 0; y < hgt_; y++, pline_data += wid_) { memcpy(line_buff_[y], pline_data, wid_ * sizeof(*pline_data)); } return true; } bool Bmp8::SaveBmp2CharDumpFile(FILE *fp) const { unsigned short wid; unsigned short hgt; unsigned short x; unsigned short y; int buf_size; int pix; int pix_cnt; unsigned int val32; unsigned char *buff; // write and check 32 bit marker val32 = kMagicNumber; if (fwrite(&val32, 1, sizeof(val32), fp) != sizeof(val32)) { return false; } // write wid and hgt wid = wid_; if (fwrite(&wid, 1, sizeof(wid), fp) != sizeof(wid)) { return false; } hgt = hgt_; if (fwrite(&hgt, 1, sizeof(hgt), fp) != sizeof(hgt)) { return false; } // write buf size pix_cnt = wid * hgt; buf_size = 3 * pix_cnt; if (fwrite(&buf_size, 1, sizeof(buf_size), fp) != sizeof(buf_size)) { return false; } // alloc memory & write the 3 channel buffer buff = new unsigned char[buf_size]; if (buff == NULL) { return false; } // copy the data for (y = 0, pix = 0; y < hgt_; y++) { for (x = 0; x < wid_; x++, pix += 3) { buff[pix] = buff[pix + 1] = buff[pix + 2] = line_buff_[y][x]; } } if (fwrite(buff, 1, buf_size, fp) != buf_size) { delete []buff; return false; } // delete temp buffer delete[]buff; return true; } // copy part of the specified bitmap to the top of the bitmap // does any necessary clipping void Bmp8::Copy(int x_st, int y_st, int wid, int hgt, Bmp8 *bmp_dest) const { int x_end = min(x_st + wid, static_cast(wid_)), y_end = min(y_st + hgt, static_cast(hgt_)); for (int y = y_st; y < y_end; y++) { for (int x = x_st; x < x_end; x++) { bmp_dest->line_buff_[y - y_st][x - x_st] = line_buff_[y][x]; } } } bool Bmp8::IsIdentical(Bmp8 *pBmp) const { if (wid_ != pBmp->wid_ || hgt_ != pBmp->hgt_) { return false; } for (int y = 0; y < hgt_; y++) { if (memcmp(line_buff_[y], pBmp->line_buff_[y], wid_) != 0) { return false; } } return true; } // Detect connected components in the bitmap ConComp ** Bmp8::FindConComps(int *concomp_cnt, int min_size) const { (*concomp_cnt) = 0; unsigned int **out_bmp_array = CreateBmpBuffer(wid_, hgt_, 0); if (out_bmp_array == NULL) { fprintf(stderr, "Cube ERROR (Bmp8::FindConComps): could not allocate " "bitmap array\n"); return NULL; } // listed of connected components ConComp **concomp_array = NULL; int x; int y; int x_nbr; int y_nbr; int concomp_id; int alloc_concomp_cnt = 0; // neighbors to check const int nbr_cnt = 4; // relative coordinates of nbrs int x_del[nbr_cnt] = {-1, 0, 1, -1}, y_del[nbr_cnt] = {-1, -1, -1, 0}; for (y = 0; y < hgt_; y++) { for (x = 0; x < wid_; x++) { // is this a foreground pix if (line_buff_[y][x] != 0xff) { int master_concomp_id = 0; ConComp *master_concomp = NULL; // checkout the nbrs for (int nbr = 0; nbr < nbr_cnt; nbr++) { x_nbr = x + x_del[nbr]; y_nbr = y + y_del[nbr]; if (x_nbr < 0 || y_nbr < 0 || x_nbr >= wid_ || y_nbr >= hgt_) { continue; } // is this nbr a foreground pix if (line_buff_[y_nbr][x_nbr] != 0xff) { // get its concomp ID concomp_id = out_bmp_array[y_nbr][x_nbr]; // this should not happen if (concomp_id < 1 || concomp_id > alloc_concomp_cnt) { fprintf(stderr, "Cube ERROR (Bmp8::FindConComps): illegal " "connected component id: %d\n", concomp_id); FreeBmpBuffer(out_bmp_array); delete []concomp_array; return NULL; } // if we has previously found a component then merge the two // and delete the latest one if (master_concomp != NULL && concomp_id != master_concomp_id) { // relabel all the pts ConCompPt *pt_ptr = concomp_array[concomp_id - 1]->Head(); while (pt_ptr != NULL) { out_bmp_array[pt_ptr->y()][pt_ptr->x()] = master_concomp_id; pt_ptr = pt_ptr->Next(); } // merge the two concomp if (!master_concomp->Merge(concomp_array[concomp_id - 1])) { fprintf(stderr, "Cube ERROR (Bmp8::FindConComps): could not " "merge connected component: %d\n", concomp_id); FreeBmpBuffer(out_bmp_array); delete []concomp_array; return NULL; } // delete the merged concomp delete concomp_array[concomp_id - 1]; concomp_array[concomp_id - 1] = NULL; } else { // this is the first concomp we encounter master_concomp_id = concomp_id; master_concomp = concomp_array[master_concomp_id - 1]; out_bmp_array[y][x] = master_concomp_id; if (!master_concomp->Add(x, y)) { fprintf(stderr, "Cube ERROR (Bmp8::FindConComps): could not " "add connected component (%d,%d)\n", x, y); FreeBmpBuffer(out_bmp_array); delete []concomp_array; return NULL; } } } // foreground nbr } // nbrs // if there was no foreground pix, then create a new concomp if (master_concomp == NULL) { master_concomp = new ConComp(); if (master_concomp == NULL || master_concomp->Add(x, y) == false) { fprintf(stderr, "Cube ERROR (Bmp8::FindConComps): could not " "allocate or add a connected component\n"); FreeBmpBuffer(out_bmp_array); delete []concomp_array; return NULL; } // extend the list of concomps if needed if ((alloc_concomp_cnt % kConCompAllocChunk) == 0) { ConComp **temp_con_comp = new ConComp *[alloc_concomp_cnt + kConCompAllocChunk]; if (temp_con_comp == NULL) { fprintf(stderr, "Cube ERROR (Bmp8::FindConComps): could not " "extend array of connected components\n"); FreeBmpBuffer(out_bmp_array); delete []concomp_array; return NULL; } if (alloc_concomp_cnt > 0) { memcpy(temp_con_comp, concomp_array, alloc_concomp_cnt * sizeof(*concomp_array)); delete []concomp_array; } concomp_array = temp_con_comp; } concomp_array[alloc_concomp_cnt++] = master_concomp; out_bmp_array[y][x] = alloc_concomp_cnt; } } // foreground pix } // x } // y // free the concomp bmp FreeBmpBuffer(out_bmp_array); if (alloc_concomp_cnt > 0 && concomp_array != NULL) { // scan the array of connected components and color // the o/p buffer with the corresponding concomps (*concomp_cnt) = 0; ConComp *concomp = NULL; for (int concomp_idx = 0; concomp_idx < alloc_concomp_cnt; concomp_idx++) { concomp = concomp_array[concomp_idx]; // found a concomp if (concomp != NULL) { // add the connected component if big enough if (concomp->PtCnt() > min_size) { concomp->SetLeftMost(true); concomp->SetRightMost(true); concomp->SetID((*concomp_cnt)); concomp_array[(*concomp_cnt)++] = concomp; } else { delete concomp; } } } } return concomp_array; } // precompute the tan table to speedup deslanting bool Bmp8::ComputeTanTable() { int ang_idx; float ang_val; // alloc memory for tan table delete []tan_table_; tan_table_ = new float[kDeslantAngleCount]; if (tan_table_ == NULL) { return false; } for (ang_idx = 0, ang_val = kMinDeslantAngle; ang_idx < kDeslantAngleCount; ang_idx++) { tan_table_[ang_idx] = tan(ang_val * M_PI / 180.0f); ang_val += kDeslantAngleDelta; } return true; } // generates a deslanted bitmap from the passed bitmap. bool Bmp8::Deslant() { int x; int y; int des_x; int des_y; int ang_idx; int best_ang; int min_des_x; int max_des_x; int des_wid; // only do deslanting if bitmap is wide enough // otherwise it slant estimate might not be reliable if (wid_ < (hgt_ * 2)) { return true; } // compute tan table if needed if (tan_table_ == NULL && !ComputeTanTable()) { return false; } // compute min and max values for x after deslant min_des_x = static_cast(0.5f + (hgt_ - 1) * tan_table_[0]); max_des_x = (wid_ - 1) + static_cast(0.5f + (hgt_ - 1) * tan_table_[kDeslantAngleCount - 1]); des_wid = max_des_x - min_des_x + 1; // alloc memory for histograms int **angle_hist = new int*[kDeslantAngleCount]; for (ang_idx = 0; ang_idx < kDeslantAngleCount; ang_idx++) { angle_hist[ang_idx] = new int[des_wid]; if (angle_hist[ang_idx] == NULL) { delete[] angle_hist; return false; } memset(angle_hist[ang_idx], 0, des_wid * sizeof(*angle_hist[ang_idx])); } // compute histograms for (y = 0; y < hgt_; y++) { for (x = 0; x < wid_; x++) { // find a non-bkgrnd pixel if (line_buff_[y][x] != 0xff) { des_y = hgt_ - y - 1; // stamp all histograms for (ang_idx = 0; ang_idx < kDeslantAngleCount; ang_idx++) { des_x = x + static_cast(0.5f + (des_y * tan_table_[ang_idx])); if (des_x >= min_des_x && des_x <= max_des_x) { angle_hist[ang_idx][des_x - min_des_x]++; } } } } } // find the histogram with the lowest entropy float entropy; double best_entropy = 0.0f; double norm_val; best_ang = -1; for (ang_idx = 0; ang_idx < kDeslantAngleCount; ang_idx++) { entropy = 0.0f; for (x = min_des_x; x <= max_des_x; x++) { if (angle_hist[ang_idx][x - min_des_x] > 0) { norm_val = (1.0f * angle_hist[ang_idx][x - min_des_x] / hgt_); entropy += (-1.0f * norm_val * log(norm_val)); } } if (best_ang == -1 || entropy < best_entropy) { best_ang = ang_idx; best_entropy = entropy; } // free the histogram delete[] angle_hist[ang_idx]; } delete[] angle_hist; // deslant if (best_ang != -1) { unsigned char **dest_lines; int old_wid = wid_; // create a new buffer wid_ = des_wid; dest_lines = CreateBmpBuffer(); if (dest_lines == NULL) { return false; } for (y = 0; y < hgt_; y++) { for (x = 0; x < old_wid; x++) { // find a non-bkgrnd pixel if (line_buff_[y][x] != 0xff) { des_y = hgt_ - y - 1; // compute new pos des_x = x + static_cast(0.5f + (des_y * tan_table_[best_ang])); dest_lines[y][des_x - min_des_x] = 0; } } } // free old buffer FreeBmpBuffer(line_buff_); line_buff_ = dest_lines; } return true; } // Load dimensions & contents of bitmap from raw data bool Bmp8::LoadFromCharDumpFile(unsigned char **raw_data_ptr) { unsigned short wid; unsigned short hgt; unsigned short x; unsigned short y; unsigned char *raw_data = (*raw_data_ptr); int buf_size; int pix; unsigned int val32; // read and check 32 bit marker memcpy(&val32, raw_data, sizeof(val32)); raw_data += sizeof(val32); if (val32 != kMagicNumber) { return false; } // read wid and hgt memcpy(&wid, raw_data, sizeof(wid)); raw_data += sizeof(wid); memcpy(&hgt, raw_data, sizeof(hgt)); raw_data += sizeof(hgt); // read buf size memcpy(&buf_size, raw_data, sizeof(buf_size)); raw_data += sizeof(buf_size); // validate buf size: for now, only 3 channel (RBG) is supported if (buf_size != (3 * wid * hgt)) { return false; } wid_ = wid; hgt_ = hgt; line_buff_ = CreateBmpBuffer(); if (line_buff_ == NULL) { return false; } // copy the data for (y = 0, pix = 0; y < hgt_; y++) { for (x = 0; x < wid_; x++, pix += 3) { // for now we only support gray scale, // so we expect R = G = B, it this is not the case, bail out if (raw_data[pix] != raw_data[pix + 1] || raw_data[pix] != raw_data[pix + 2]) { return false; } line_buff_[y][x] = raw_data[pix]; } } (*raw_data_ptr) = raw_data + buf_size; return true; } float Bmp8::ForegroundRatio() const { int fore_cnt = 0; if (wid_ == 0 || hgt_ == 0) { return 1.0; } for (int y = 0; y < hgt_; y++) { for (int x = 0; x < wid_; x++) { fore_cnt += (line_buff_[y][x] == 0xff ? 0 : 1); } } return (1.0 * (fore_cnt / hgt_) / wid_); } // generates a deslanted bitmap from the passed bitmap bool Bmp8::HorizontalDeslant(double *deslant_angle) { int x; int y; int des_y; int ang_idx; int best_ang; int min_des_y; int max_des_y; int des_hgt; // compute tan table if necess. if (tan_table_ == NULL && !ComputeTanTable()) { return false; } // compute min and max values for x after deslant min_des_y = min(0, static_cast((wid_ - 1) * tan_table_[0])); max_des_y = (hgt_ - 1) + max(0, static_cast((wid_ - 1) * tan_table_[kDeslantAngleCount - 1])); des_hgt = max_des_y - min_des_y + 1; // alloc memory for histograms int **angle_hist = new int*[kDeslantAngleCount]; for (ang_idx = 0; ang_idx < kDeslantAngleCount; ang_idx++) { angle_hist[ang_idx] = new int[des_hgt]; if (angle_hist[ang_idx] == NULL) { delete[] angle_hist; return false; } memset(angle_hist[ang_idx], 0, des_hgt * sizeof(*angle_hist[ang_idx])); } // compute histograms for (y = 0; y < hgt_; y++) { for (x = 0; x < wid_; x++) { // find a non-bkgrnd pixel if (line_buff_[y][x] != 0xff) { // stamp all histograms for (ang_idx = 0; ang_idx < kDeslantAngleCount; ang_idx++) { des_y = y - static_cast(x * tan_table_[ang_idx]); if (des_y >= min_des_y && des_y <= max_des_y) { angle_hist[ang_idx][des_y - min_des_y]++; } } } } } // find the histogram with the lowest entropy float entropy; float best_entropy = 0.0f; float norm_val; best_ang = -1; for (ang_idx = 0; ang_idx < kDeslantAngleCount; ang_idx++) { entropy = 0.0f; for (y = min_des_y; y <= max_des_y; y++) { if (angle_hist[ang_idx][y - min_des_y] > 0) { norm_val = (1.0f * angle_hist[ang_idx][y - min_des_y] / wid_); entropy += (-1.0f * norm_val * log(norm_val)); } } if (best_ang == -1 || entropy < best_entropy) { best_ang = ang_idx; best_entropy = entropy; } // free the histogram delete[] angle_hist[ang_idx]; } delete[] angle_hist; (*deslant_angle) = 0.0; // deslant if (best_ang != -1) { unsigned char **dest_lines; int old_hgt = hgt_; // create a new buffer min_des_y = min(0, static_cast((wid_ - 1) * -tan_table_[best_ang])); max_des_y = (hgt_ - 1) + max(0, static_cast((wid_ - 1) * -tan_table_[best_ang])); hgt_ = max_des_y - min_des_y + 1; dest_lines = CreateBmpBuffer(); if (dest_lines == NULL) { return false; } for (y = 0; y < old_hgt; y++) { for (x = 0; x < wid_; x++) { // find a non-bkgrnd pixel if (line_buff_[y][x] != 0xff) { // compute new pos des_y = y - static_cast((x * tan_table_[best_ang])); dest_lines[des_y - min_des_y][x] = 0; } } } // free old buffer FreeBmpBuffer(line_buff_); line_buff_ = dest_lines; (*deslant_angle) = kMinDeslantAngle + (best_ang * kDeslantAngleDelta); } return true; } float Bmp8::MeanHorizontalHistogramEntropy() const { float entropy = 0.0f; // compute histograms for (int y = 0; y < hgt_; y++) { int pix_cnt = 0; for (int x = 0; x < wid_; x++) { // find a non-bkgrnd pixel if (line_buff_[y][x] != 0xff) { pix_cnt++; } } if (pix_cnt > 0) { float norm_val = (1.0f * pix_cnt / wid_); entropy += (-1.0f * norm_val * log(norm_val)); } } return entropy / hgt_; } int *Bmp8::HorizontalHistogram() const { int *hist = new int[hgt_]; if (hist == NULL) { return NULL; } // compute histograms for (int y = 0; y < hgt_; y++) { hist[y] = 0; for (int x = 0; x < wid_; x++) { // find a non-bkgrnd pixel if (line_buff_[y][x] != 0xff) { hist[y]++; } } } return hist; } } // namespace tesseract tesseract-3.04.01/cube/bmp_8.h000066400000000000000000000106721266071204500157610ustar00rootroot00000000000000/********************************************************************** * File: bmp_8.h * Description: Declaration of an 8-bit Bitmap class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 BMP8_H #define BMP8_H // The Bmp8 class is an 8-bit bitmap that represents images of // words, characters and segments throughout Cube // It is meant to provide fast access to the bitmap bits and provide // fast scaling, cropping, deslanting, connected components detection, // loading and saving functionality #include #include #include "con_comp.h" #include "cached_file.h" namespace tesseract { // Non-integral deslanting parameters. static const float kMinDeslantAngle = -30.0f; static const float kMaxDeslantAngle = 30.0f; static const float kDeslantAngleDelta = 0.5f; class Bmp8 { public: Bmp8(unsigned short wid, unsigned short hgt); ~Bmp8(); // Clears the bitmap bool Clear(); // accessors to bitmap dimensions inline unsigned short Width() const { return wid_; } inline unsigned short Stride() const { return stride_; } inline unsigned short Height() const { return hgt_; } inline unsigned char *RawData() const { return (line_buff_ == NULL ? NULL : line_buff_[0]); } // creates a scaled version of the specified bitmap // Optionally, scaling can be isotropic (preserving aspect ratio) or not bool ScaleFrom(Bmp8 *bmp, bool isotropic = true); // Deslant the bitmap vertically bool Deslant(); // Deslant the bitmap horizontally bool HorizontalDeslant(double *deslant_angle); // Create a bitmap object from a file static Bmp8 *FromCharDumpFile(CachedFile *fp); static Bmp8 *FromCharDumpFile(FILE *fp); // are two bitmaps identical bool IsIdentical(Bmp8 *pBmp) const; // Detect connected components ConComp ** FindConComps(int *concomp_cnt, int min_size) const; // compute the foreground ratio float ForegroundRatio() const; // returns the mean horizontal histogram entropy of the bitmap float MeanHorizontalHistogramEntropy() const; // returns the horizontal histogram of the bitmap int *HorizontalHistogram() const; private: // Compute a look up tan table that will be used for fast slant computation static bool ComputeTanTable(); // create a bitmap buffer (two flavors char & int) and init contents unsigned char ** CreateBmpBuffer(unsigned char init_val = 0xff); static unsigned int ** CreateBmpBuffer(int wid, int hgt, unsigned char init_val = 0xff); // Free a bitmap buffer static void FreeBmpBuffer(unsigned char **buff); static void FreeBmpBuffer(unsigned int **buff); // a static array that holds the tan lookup table static float *tan_table_; // bitmap 32-bit-aligned stride unsigned short stride_; // Bmp8 magic number used to validate saved bitmaps static const unsigned int kMagicNumber = 0xdeadbeef; protected: // bitmap dimensions unsigned short wid_; unsigned short hgt_; // bitmap contents unsigned char **line_buff_; // deslanting parameters static const int kConCompAllocChunk = 16; static const int kDeslantAngleCount; // Load dimensions & contents of bitmap from file bool LoadFromCharDumpFile(CachedFile *fp); bool LoadFromCharDumpFile(FILE *fp); // Load dimensions & contents of bitmap from raw data bool LoadFromCharDumpFile(unsigned char **raw_data); // Load contents of bitmap from raw data bool LoadFromRawData(unsigned char *data); // save bitmap to a file bool SaveBmp2CharDumpFile(FILE *fp) const; // checks if a row or a column are entirely blank bool IsBlankColumn(int x) const; bool IsBlankRow(int y) const; // crop the bitmap returning new dimensions void Crop(int *xst_src, int *yst_src, int *wid, int *hgt); // copy part of the specified bitmap void Copy(int x, int y, int wid, int hgt, Bmp8 *bmp_dest) const; }; } #endif // BMP8_H tesseract-3.04.01/cube/cached_file.cpp000066400000000000000000000064271266071204500175200ustar00rootroot00000000000000/********************************************************************** * File: cached_file.pp * Description: Implementation of an Cached File Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 #include #include "cached_file.h" namespace tesseract { CachedFile::CachedFile(string file_name) { file_name_ = file_name; buff_ = NULL; buff_pos_ = 0; buff_size_ = 0; file_pos_ = 0; file_size_ = 0; fp_ = NULL; } CachedFile::~CachedFile() { if (fp_ != NULL) { fclose(fp_); fp_ = NULL; } if (buff_ != NULL) { delete []buff_; buff_ = NULL; } } // free buffers and init vars bool CachedFile::Open() { if (fp_ != NULL) { return true; } fp_ = fopen(file_name_.c_str(), "rb"); if (fp_ == NULL) { return false; } // seek to the end fseek(fp_, 0, SEEK_END); // get file size file_size_ = ftell(fp_); if (file_size_ < 1) { return false; } // rewind again rewind(fp_); // alloc memory for buffer buff_ = new unsigned char[kCacheSize]; if (buff_ == NULL) { return false; } // init counters buff_size_ = 0; buff_pos_ = 0; file_pos_ = 0; return true; } // add a new sample int CachedFile::Read(void *read_buff, int bytes) { int read_bytes = 0; unsigned char *buff = (unsigned char *)read_buff; // do we need to read beyond the buffer if ((buff_pos_ + bytes) > buff_size_) { // copy as much bytes from the current buffer if any int copy_bytes = buff_size_ - buff_pos_; if (copy_bytes > 0) { memcpy(buff, buff_ + buff_pos_, copy_bytes); buff += copy_bytes; bytes -= copy_bytes; read_bytes += copy_bytes; } // determine how much to read buff_size_ = kCacheSize; if ((file_pos_ + buff_size_) > file_size_) { buff_size_ = static_cast(file_size_ - file_pos_); } // EOF ? if (buff_size_ <= 0 || bytes > buff_size_) { return read_bytes; } // read the first chunck if (fread(buff_, 1, buff_size_, fp_) != buff_size_) { return read_bytes; } buff_pos_ = 0; file_pos_ += buff_size_; } memcpy(buff, buff_ + buff_pos_, bytes); read_bytes += bytes; buff_pos_ += bytes; return read_bytes; } long CachedFile::Size() { if (fp_ == NULL && Open() == false) { return 0; } return file_size_; } long CachedFile::Tell() { if (fp_ == NULL && Open() == false) { return 0; } return file_pos_ - buff_size_ + buff_pos_; } bool CachedFile::eof() { if (fp_ == NULL && Open() == false) { return true; } return (file_pos_ - buff_size_ + buff_pos_) >= file_size_; } } // namespace tesseract tesseract-3.04.01/cube/cached_file.h000066400000000000000000000036231266071204500171600ustar00rootroot00000000000000/********************************************************************** * File: cached_file.h * Description: Declaration of a Cached File class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 CACHED_FILE_H #define CACHED_FILE_H // The CachedFile class provides a large-cache read access to a file // It is mainly designed for loading large word dump files #include #include #ifdef USE_STD_NAMESPACE using std::string; #endif namespace tesseract { class CachedFile { public: explicit CachedFile(string file_name); ~CachedFile(); // reads a specified number of bytes to the specified buffer and // returns the actual number of bytes read int Read(void *read_buff, int bytes); // Returns the file size long Size(); // returns the current position in the file long Tell(); // End of file flag bool eof(); private: static const unsigned int kCacheSize = 0x8000000; // file name string file_name_; // internal file buffer unsigned char *buff_; // file position long file_pos_; // file size long file_size_; // position of file within buffer int buff_pos_; // buffer size int buff_size_; // file handle FILE *fp_; // Opens the file bool Open(); }; } #endif // CACHED_FILE_H tesseract-3.04.01/cube/char_altlist.cpp000066400000000000000000000061131266071204500177530ustar00rootroot00000000000000/********************************************************************** * File: char_altlist.cpp * Description: Implementation of a Character Alternate List Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 "char_altlist.h" namespace tesseract { // The CharSet is not class owned and must exist for // the life time of this class CharAltList::CharAltList(const CharSet *char_set, int max_alt) : AltList(max_alt) { char_set_ = char_set; max_alt_ = max_alt; class_id_alt_ = NULL; class_id_cost_ = NULL; } CharAltList::~CharAltList() { if (class_id_alt_ != NULL) { delete []class_id_alt_; class_id_alt_ = NULL; } if (class_id_cost_ != NULL) { delete []class_id_cost_; class_id_cost_ = NULL; } } // Insert a new char alternate bool CharAltList::Insert(int class_id, int cost, void *tag) { // validate class ID if (class_id < 0 || class_id >= char_set_->ClassCount()) { return false; } // allocate buffers if nedded if (class_id_alt_ == NULL || alt_cost_ == NULL) { class_id_alt_ = new int[max_alt_]; alt_cost_ = new int[max_alt_]; alt_tag_ = new void *[max_alt_]; if (class_id_alt_ == NULL || alt_cost_ == NULL || alt_tag_ == NULL) { return false; } memset(alt_tag_, 0, max_alt_ * sizeof(*alt_tag_)); } if (class_id_cost_ == NULL) { int class_cnt = char_set_->ClassCount(); class_id_cost_ = new int[class_cnt]; if (class_id_cost_ == NULL) { return false; } for (int ich = 0; ich < class_cnt; ich++) { class_id_cost_[ich] = WORST_COST; } } if (class_id < 0 || class_id >= char_set_->ClassCount()) { return false; } // insert the alternate class_id_alt_[alt_cnt_] = class_id; alt_cost_[alt_cnt_] = cost; alt_tag_[alt_cnt_] = tag; alt_cnt_++; class_id_cost_[class_id] = cost; return true; } // sort the alternate Desc. based on prob void CharAltList::Sort() { for (int alt_idx = 0; alt_idx < alt_cnt_; alt_idx++) { for (int alt = alt_idx + 1; alt < alt_cnt_; alt++) { if (alt_cost_[alt_idx] > alt_cost_[alt]) { int temp = class_id_alt_[alt_idx]; class_id_alt_[alt_idx] = class_id_alt_[alt]; class_id_alt_[alt] = temp; temp = alt_cost_[alt_idx]; alt_cost_[alt_idx] = alt_cost_[alt]; alt_cost_[alt] = temp; void *tag = alt_tag_[alt_idx]; alt_tag_[alt_idx] = alt_tag_[alt]; alt_tag_[alt] = tag; } } } } } tesseract-3.04.01/cube/char_altlist.h000066400000000000000000000046371266071204500174310ustar00rootroot00000000000000/********************************************************************** * File: char_altlist.h * Description: Declaration of a Character Alternate List Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 CHAR_ALT_LIST_H #define CHAR_ALT_LIST_H // The CharAltList class holds the list of class alternates returned from // a character classifier. Each alternate represents a class ID. // It inherits from the AltList class. // The CharAltList owns a CharSet object that maps a class-id to a string. #include "altlist.h" #include "char_set.h" namespace tesseract { class CharAltList : public AltList { public: CharAltList(const CharSet *char_set, int max_alt = kMaxCharAlt); ~CharAltList(); // Sort the alternate list based on cost void Sort(); // insert a new alternate with the specified class-id, cost and tag bool Insert(int class_id, int cost, void *tag = NULL); // returns the cost of a specific class ID inline int ClassCost(int class_id) const { if (class_id_cost_ == NULL || class_id < 0 || class_id >= char_set_->ClassCount()) { return WORST_COST; } return class_id_cost_[class_id]; } // returns the alternate class-id corresponding to an alternate index inline int Alt(int alt_idx) const { return class_id_alt_[alt_idx]; } // set the cost of a certain alternate void SetAltCost(int alt_idx, int cost) { alt_cost_[alt_idx] = cost; class_id_cost_[class_id_alt_[alt_idx]] = cost; } private: // character set object. Passed at construction time const CharSet *char_set_; // array of alternate class-ids int *class_id_alt_; // array of alternate costs int *class_id_cost_; // default max count of alternates static const int kMaxCharAlt = 256; }; } #endif // CHAR_ALT_LIST_H tesseract-3.04.01/cube/char_bigrams.cpp000066400000000000000000000151031266071204500177220ustar00rootroot00000000000000/********************************************************************** * File: char_bigrams.cpp * Description: Implementation of a Character Bigrams Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 #include #include #include "char_bigrams.h" #include "cube_utils.h" #include "ndminx.h" #include "cube_const.h" namespace tesseract { CharBigrams::CharBigrams() { memset(&bigram_table_, 0, sizeof(bigram_table_)); } CharBigrams::~CharBigrams() { if (bigram_table_.char_bigram != NULL) { for (int ch1 = 0; ch1 <= bigram_table_.max_char; ch1++) { CharBigram *char_bigram = bigram_table_.char_bigram + ch1; if (char_bigram->bigram != NULL) { delete []char_bigram->bigram; } } delete []bigram_table_.char_bigram; } } CharBigrams *CharBigrams::Create(const string &data_file_path, const string &lang) { string file_name; string str; file_name = data_file_path + lang; file_name += ".cube.bigrams"; // load the string into memory if (!CubeUtils::ReadFileToString(file_name, &str)) { return NULL; } // construct a new object CharBigrams *char_bigrams_obj = new CharBigrams(); if (char_bigrams_obj == NULL) { fprintf(stderr, "Cube ERROR (CharBigrams::Create): could not create " "character bigrams object.\n"); return NULL; } CharBigramTable *table = &char_bigrams_obj->bigram_table_; table->total_cnt = 0; table->max_char = -1; table->char_bigram = NULL; // split into lines vector str_vec; CubeUtils::SplitStringUsing(str, "\r\n", &str_vec); for (int big = 0; big < str_vec.size(); big++) { char_32 ch1; char_32 ch2; int cnt; if (sscanf(str_vec[big].c_str(), "%d %x %x", &cnt, &ch1, &ch2) != 3) { fprintf(stderr, "Cube ERROR (CharBigrams::Create): invalid format " "reading line: %s\n", str_vec[big].c_str()); delete char_bigrams_obj; return NULL; } // expand the bigram table if (ch1 > table->max_char) { CharBigram *char_bigram = new CharBigram[ch1 + 1]; if (char_bigram == NULL) { fprintf(stderr, "Cube ERROR (CharBigrams::Create): error allocating " "additional memory for character bigram table.\n"); return NULL; } if (table->char_bigram != NULL && table->max_char >= 0) { memcpy(char_bigram, table->char_bigram, (table->max_char + 1) * sizeof(*char_bigram)); delete []table->char_bigram; } table->char_bigram = char_bigram; // init for (int new_big = table->max_char + 1; new_big <= ch1; new_big++) { table->char_bigram[new_big].total_cnt = 0; table->char_bigram[new_big].max_char = -1; table->char_bigram[new_big].bigram = NULL; } table->max_char = ch1; } if (ch2 > table->char_bigram[ch1].max_char) { Bigram *bigram = new Bigram[ch2 + 1]; if (bigram == NULL) { fprintf(stderr, "Cube ERROR (CharBigrams::Create): error allocating " "memory for bigram.\n"); delete char_bigrams_obj; return NULL; } if (table->char_bigram[ch1].bigram != NULL && table->char_bigram[ch1].max_char >= 0) { memcpy(bigram, table->char_bigram[ch1].bigram, (table->char_bigram[ch1].max_char + 1) * sizeof(*bigram)); delete []table->char_bigram[ch1].bigram; } table->char_bigram[ch1].bigram = bigram; // init for (int new_big = table->char_bigram[ch1].max_char + 1; new_big <= ch2; new_big++) { table->char_bigram[ch1].bigram[new_big].cnt = 0; } table->char_bigram[ch1].max_char = ch2; } table->char_bigram[ch1].bigram[ch2].cnt = cnt; table->char_bigram[ch1].total_cnt += cnt; table->total_cnt += cnt; } // compute costs (-log probs) table->worst_cost = static_cast( -PROB2COST_SCALE * log(0.5 / table->total_cnt)); for (char_32 ch1 = 0; ch1 <= table->max_char; ch1++) { for (char_32 ch2 = 0; ch2 <= table->char_bigram[ch1].max_char; ch2++) { int cnt = table->char_bigram[ch1].bigram[ch2].cnt; table->char_bigram[ch1].bigram[ch2].cost = static_cast(-PROB2COST_SCALE * log(MAX(0.5, static_cast(cnt)) / table->total_cnt)); } } return char_bigrams_obj; } int CharBigrams::PairCost(char_32 ch1, char_32 ch2) const { if (ch1 > bigram_table_.max_char) { return bigram_table_.worst_cost; } if (ch2 > bigram_table_.char_bigram[ch1].max_char) { return bigram_table_.worst_cost; } return bigram_table_.char_bigram[ch1].bigram[ch2].cost; } int CharBigrams::Cost(const char_32 *char_32_ptr, CharSet *char_set) const { if (!char_32_ptr || char_32_ptr[0] == 0) { return bigram_table_.worst_cost; } int cost = MeanCostWithSpaces(char_32_ptr); if (CubeUtils::StrLen(char_32_ptr) >= kMinLengthCaseInvariant && CubeUtils::IsCaseInvariant(char_32_ptr, char_set)) { char_32 *lower_32 = CubeUtils::ToLower(char_32_ptr, char_set); if (lower_32 && lower_32[0] != 0) { int cost_lower = MeanCostWithSpaces(lower_32); cost = MIN(cost, cost_lower); delete [] lower_32; } char_32 *upper_32 = CubeUtils::ToUpper(char_32_ptr, char_set); if (upper_32 && upper_32[0] != 0) { int cost_upper = MeanCostWithSpaces(upper_32); cost = MIN(cost, cost_upper); delete [] upper_32; } } return cost; } int CharBigrams::MeanCostWithSpaces(const char_32 *char_32_ptr) const { if (!char_32_ptr) return bigram_table_.worst_cost; int len = CubeUtils::StrLen(char_32_ptr); int cost = 0; int c = 0; cost = PairCost(' ', char_32_ptr[0]); for (c = 1; c < len; c++) { cost += PairCost(char_32_ptr[c - 1], char_32_ptr[c]); } cost += PairCost(char_32_ptr[len - 1], ' '); return static_cast(cost / static_cast(len + 1)); } } // namespace tesseract tesseract-3.04.01/cube/char_bigrams.h000066400000000000000000000055201266071204500173710ustar00rootroot00000000000000/********************************************************************** * File: char_bigrams.h * Description: Declaration of a Character Bigrams Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The CharBigram class represents the interface to the character bigram // table used by Cube // A CharBigram object can be constructed from the Char Bigrams file // Given a sequence of characters, the "Cost" method returns the Char Bigram // cost of the string according to the table #ifndef CHAR_BIGRAMS_H #define CHAR_BIGRAMS_H #include #include "char_set.h" namespace tesseract { // structure representing a single bigram value struct Bigram { int cnt; int cost; }; // structure representing the char bigram array of characters // following a specific character struct CharBigram { int total_cnt; char_32 max_char; Bigram *bigram; }; // structure representing the whole bigram table struct CharBigramTable { int total_cnt; int worst_cost; char_32 max_char; CharBigram *char_bigram; }; class CharBigrams { public: CharBigrams(); ~CharBigrams(); // Construct the CharBigrams class from a file static CharBigrams *Create(const string &data_file_path, const string &lang); // Top-level function to return the mean character bigram cost of a // sequence of characters. If char_set is not NULL, use // tesseract functions to return a case-invariant cost. // This avoids unnecessarily penalizing all-one-case words or // capitalized words (first-letter upper-case and remaining letters // lower-case). int Cost(const char_32 *str, CharSet *char_set) const; protected: // Returns the character bigram cost of two characters. int PairCost(char_32 ch1, char_32 ch2) const; // Returns the mean character bigram cost of a sequence of // characters. Adds a space at the beginning and end to account for // cost of starting and ending characters. int MeanCostWithSpaces(const char_32 *char_32_ptr) const; private: // Only words this length or greater qualify for case-invariant character // bigram cost. static const int kMinLengthCaseInvariant = 4; CharBigramTable bigram_table_; }; } #endif // CHAR_BIGRAMS_H tesseract-3.04.01/cube/char_samp.cpp000066400000000000000000000504251266071204500172440ustar00rootroot00000000000000/********************************************************************** * File: char_samp.cpp * Description: Implementation of a Character Bitmap Sample Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 #include "char_samp.h" #include "cube_utils.h" namespace tesseract { #define MAX_LINE_LEN 1024 CharSamp::CharSamp() : Bmp8(0, 0) { left_ = 0; top_ = 0; label32_ = NULL; page_ = -1; } CharSamp::CharSamp(int wid, int hgt) : Bmp8(wid, hgt) { left_ = 0; top_ = 0; label32_ = NULL; page_ = -1; } CharSamp::CharSamp(int left, int top, int wid, int hgt) : Bmp8(wid, hgt) , left_(left) , top_(top) { label32_ = NULL; page_ = -1; } CharSamp::~CharSamp() { if (label32_ != NULL) { delete []label32_; label32_ = NULL; } } // returns a UTF-8 version of the string label string CharSamp::stringLabel() const { string str = ""; if (label32_ != NULL) { string_32 str32(label32_); CubeUtils::UTF32ToUTF8(str32.c_str(), &str); } return str; } // set a the string label using a UTF encoded string void CharSamp::SetLabel(string str) { if (label32_ != NULL) { delete []label32_; label32_ = NULL; } string_32 str32; CubeUtils::UTF8ToUTF32(str.c_str(), &str32); SetLabel(reinterpret_cast(str32.c_str())); } // creates a CharSamp object from file CharSamp *CharSamp::FromCharDumpFile(CachedFile *fp) { unsigned short left; unsigned short top; unsigned short page; unsigned short first_char; unsigned short last_char; unsigned short norm_top; unsigned short norm_bottom; unsigned short norm_aspect_ratio; unsigned int val32; char_32 *label32; // read and check 32 bit marker if (fp->Read(&val32, sizeof(val32)) != sizeof(val32)) { return NULL; } if (val32 != 0xabd0fefe) { return NULL; } // read label length, if (fp->Read(&val32, sizeof(val32)) != sizeof(val32)) { return NULL; } // the label is not null terminated in the file if (val32 > 0 && val32 < MAX_UINT32) { label32 = new char_32[val32 + 1]; if (label32 == NULL) { return NULL; } // read label if (fp->Read(label32, val32 * sizeof(*label32)) != (val32 * sizeof(*label32))) { return NULL; } // null terminate label32[val32] = 0; } else { label32 = NULL; } // read coordinates if (fp->Read(&page, sizeof(page)) != sizeof(page)) { return NULL; } if (fp->Read(&left, sizeof(left)) != sizeof(left)) { return NULL; } if (fp->Read(&top, sizeof(top)) != sizeof(top)) { return NULL; } if (fp->Read(&first_char, sizeof(first_char)) != sizeof(first_char)) { return NULL; } if (fp->Read(&last_char, sizeof(last_char)) != sizeof(last_char)) { return NULL; } if (fp->Read(&norm_top, sizeof(norm_top)) != sizeof(norm_top)) { return NULL; } if (fp->Read(&norm_bottom, sizeof(norm_bottom)) != sizeof(norm_bottom)) { return NULL; } if (fp->Read(&norm_aspect_ratio, sizeof(norm_aspect_ratio)) != sizeof(norm_aspect_ratio)) { return NULL; } // create the object CharSamp *char_samp = new CharSamp(); if (char_samp == NULL) { return NULL; } // init char_samp->label32_ = label32; char_samp->page_ = page; char_samp->left_ = left; char_samp->top_ = top; char_samp->first_char_ = first_char; char_samp->last_char_ = last_char; char_samp->norm_top_ = norm_top; char_samp->norm_bottom_ = norm_bottom; char_samp->norm_aspect_ratio_ = norm_aspect_ratio; // load the Bmp8 part if (char_samp->LoadFromCharDumpFile(fp) == false) { delete char_samp; return NULL; } return char_samp; } // Load a Char Samp from a dump file CharSamp *CharSamp::FromCharDumpFile(FILE *fp) { unsigned short left; unsigned short top; unsigned short page; unsigned short first_char; unsigned short last_char; unsigned short norm_top; unsigned short norm_bottom; unsigned short norm_aspect_ratio; unsigned int val32; char_32 *label32; // read and check 32 bit marker if (fread(&val32, 1, sizeof(val32), fp) != sizeof(val32)) { return NULL; } if (val32 != 0xabd0fefe) { return NULL; } // read label length, if (fread(&val32, 1, sizeof(val32), fp) != sizeof(val32)) { return NULL; } // the label is not null terminated in the file if (val32 > 0 && val32 < MAX_UINT32) { label32 = new char_32[val32 + 1]; if (label32 == NULL) { return NULL; } // read label if (fread(label32, 1, val32 * sizeof(*label32), fp) != (val32 * sizeof(*label32))) { delete [] label32; return NULL; } // null terminate label32[val32] = 0; } else { label32 = NULL; } // read coordinates if (fread(&page, 1, sizeof(page), fp) != sizeof(page) || fread(&left, 1, sizeof(left), fp) != sizeof(left) || fread(&top, 1, sizeof(top), fp) != sizeof(top) || fread(&first_char, 1, sizeof(first_char), fp) != sizeof(first_char) || fread(&last_char, 1, sizeof(last_char), fp) != sizeof(last_char) || fread(&norm_top, 1, sizeof(norm_top), fp) != sizeof(norm_top) || fread(&norm_bottom, 1, sizeof(norm_bottom), fp) != sizeof(norm_bottom) || fread(&norm_aspect_ratio, 1, sizeof(norm_aspect_ratio), fp) != sizeof(norm_aspect_ratio)) { delete [] label32; return NULL; } // create the object CharSamp *char_samp = new CharSamp(); if (char_samp == NULL) { delete [] label32; return NULL; } // init char_samp->label32_ = label32; char_samp->page_ = page; char_samp->left_ = left; char_samp->top_ = top; char_samp->first_char_ = first_char; char_samp->last_char_ = last_char; char_samp->norm_top_ = norm_top; char_samp->norm_bottom_ = norm_bottom; char_samp->norm_aspect_ratio_ = norm_aspect_ratio; // load the Bmp8 part if (char_samp->LoadFromCharDumpFile(fp) == false) { delete char_samp; // It owns label32. return NULL; } return char_samp; } // returns a copy of the charsamp that is scaled to the // specified width and height CharSamp *CharSamp::Scale(int wid, int hgt, bool isotropic) { CharSamp *scaled_samp = new CharSamp(wid, hgt); if (scaled_samp == NULL) { return NULL; } if (scaled_samp->ScaleFrom(this, isotropic) == false) { delete scaled_samp; return NULL; } scaled_samp->left_ = left_; scaled_samp->top_ = top_; scaled_samp->page_ = page_; scaled_samp->SetLabel(label32_); scaled_samp->first_char_ = first_char_; scaled_samp->last_char_ = last_char_; scaled_samp->norm_top_ = norm_top_; scaled_samp->norm_bottom_ = norm_bottom_; scaled_samp->norm_aspect_ratio_ = norm_aspect_ratio_; return scaled_samp; } // Load a Char Samp from a dump file CharSamp *CharSamp::FromRawData(int left, int top, int wid, int hgt, unsigned char *data) { // create the object CharSamp *char_samp = new CharSamp(left, top, wid, hgt); if (char_samp == NULL) { return NULL; } if (char_samp->LoadFromRawData(data) == false) { delete char_samp; return NULL; } return char_samp; } // Saves the charsamp to a dump file bool CharSamp::Save2CharDumpFile(FILE *fp) const { unsigned int val32; // write and check 32 bit marker val32 = 0xabd0fefe; if (fwrite(&val32, 1, sizeof(val32), fp) != sizeof(val32)) { return false; } // write label length val32 = (label32_ == NULL) ? 0 : LabelLen(label32_); if (fwrite(&val32, 1, sizeof(val32), fp) != sizeof(val32)) { return false; } // write label if (label32_ != NULL) { if (fwrite(label32_, 1, val32 * sizeof(*label32_), fp) != (val32 * sizeof(*label32_))) { return false; } } // write coordinates if (fwrite(&page_, 1, sizeof(page_), fp) != sizeof(page_)) { return false; } if (fwrite(&left_, 1, sizeof(left_), fp) != sizeof(left_)) { return false; } if (fwrite(&top_, 1, sizeof(top_), fp) != sizeof(top_)) { return false; } if (fwrite(&first_char_, 1, sizeof(first_char_), fp) != sizeof(first_char_)) { return false; } if (fwrite(&last_char_, 1, sizeof(last_char_), fp) != sizeof(last_char_)) { return false; } if (fwrite(&norm_top_, 1, sizeof(norm_top_), fp) != sizeof(norm_top_)) { return false; } if (fwrite(&norm_bottom_, 1, sizeof(norm_bottom_), fp) != sizeof(norm_bottom_)) { return false; } if (fwrite(&norm_aspect_ratio_, 1, sizeof(norm_aspect_ratio_), fp) != sizeof(norm_aspect_ratio_)) { return false; } if (SaveBmp2CharDumpFile(fp) == false) { return false; } return true; } // Crop the char samp such that there are no white spaces on any side. // The norm_top_ and norm_bottom_ fields are the character top/bottom // with respect to whatever context the character is being recognized // in (e.g. word bounding box) normalized to a standard size of // 255. Here they default to 0 and 255 (word box boundaries), but // since they are context dependent, they may need to be reset by the // calling function. CharSamp *CharSamp::Crop() { // get the dimesions of the cropped img int cropped_left = 0; int cropped_top = 0; int cropped_wid = wid_; int cropped_hgt = hgt_; Bmp8::Crop(&cropped_left, &cropped_top, &cropped_wid, &cropped_hgt); if (cropped_wid == 0 || cropped_hgt == 0) { return NULL; } // create the cropped char samp CharSamp *cropped_samp = new CharSamp(left_ + cropped_left, top_ + cropped_top, cropped_wid, cropped_hgt); cropped_samp->SetLabel(label32_); cropped_samp->SetFirstChar(first_char_); cropped_samp->SetLastChar(last_char_); // the following 3 fields may/should be reset by the calling function // using context information, i.e., location of character box // w.r.t. the word bounding box cropped_samp->SetNormAspectRatio(255 * cropped_wid / (cropped_wid + cropped_hgt)); cropped_samp->SetNormTop(0); cropped_samp->SetNormBottom(255); // copy the bitmap to the cropped img Copy(cropped_left, cropped_top, cropped_wid, cropped_hgt, cropped_samp); return cropped_samp; } // segment the char samp to connected components // based on contiguity and vertical pixel density histogram ConComp **CharSamp::Segment(int *segment_cnt, bool right_2_left, int max_hist_wnd, int min_con_comp_size) const { // init (*segment_cnt) = 0; int concomp_cnt = 0; int seg_cnt = 0; // find the concomps of the image ConComp **concomp_array = FindConComps(&concomp_cnt, min_con_comp_size); if (concomp_cnt <= 0 || !concomp_array) { if (concomp_array) delete []concomp_array; return NULL; } ConComp **seg_array = NULL; // segment each concomp further using vertical histogram for (int concomp = 0; concomp < concomp_cnt; concomp++) { int concomp_seg_cnt = 0; // segment the concomp ConComp **concomp_seg_array = NULL; ConComp **concomp_alloc_seg = concomp_array[concomp]->Segment(max_hist_wnd, &concomp_seg_cnt); // no segments, add the whole concomp if (concomp_alloc_seg == NULL) { concomp_seg_cnt = 1; concomp_seg_array = concomp_array + concomp; } else { // delete the original concomp, we no longer need it concomp_seg_array = concomp_alloc_seg; delete concomp_array[concomp]; } // add the resulting segments for (int seg_idx = 0; seg_idx < concomp_seg_cnt; seg_idx++) { // too small of a segment: ignore if (concomp_seg_array[seg_idx]->Width() < 2 && concomp_seg_array[seg_idx]->Height() < 2) { delete concomp_seg_array[seg_idx]; } else { // add the new segment // extend the segment array if ((seg_cnt % kConCompAllocChunk) == 0) { ConComp **temp_segm_array = new ConComp *[seg_cnt + kConCompAllocChunk]; if (temp_segm_array == NULL) { fprintf(stderr, "Cube ERROR (CharSamp::Segment): could not " "allocate additional connected components\n"); delete []concomp_seg_array; delete []concomp_array; delete []seg_array; return NULL; } if (seg_cnt > 0) { memcpy(temp_segm_array, seg_array, seg_cnt * sizeof(*seg_array)); delete []seg_array; } seg_array = temp_segm_array; } seg_array[seg_cnt++] = concomp_seg_array[seg_idx]; } } // segment if (concomp_alloc_seg != NULL) { delete []concomp_alloc_seg; } } // concomp delete []concomp_array; // sort the concomps from Left2Right or Right2Left, based on the reading order if (seg_cnt > 0 && seg_array != NULL) { qsort(seg_array, seg_cnt, sizeof(*seg_array), right_2_left ? ConComp::Right2LeftComparer : ConComp::Left2RightComparer); } (*segment_cnt) = seg_cnt; return seg_array; } // builds a char samp from a set of connected components CharSamp *CharSamp::FromConComps(ConComp **concomp_array, int strt_concomp, int seg_flags_size, int *seg_flags, bool *left_most, bool *right_most, int word_hgt) { int concomp; int end_concomp; int concomp_cnt = 0; end_concomp = strt_concomp + seg_flags_size; // determine ID range bool once = false; int min_id = -1; int max_id = -1; for (concomp = strt_concomp; concomp < end_concomp; concomp++) { if (!seg_flags || seg_flags[concomp - strt_concomp] != 0) { if (!once) { min_id = concomp_array[concomp]->ID(); max_id = concomp_array[concomp]->ID(); once = true; } else { UpdateRange(concomp_array[concomp]->ID(), &min_id, &max_id); } concomp_cnt++; } } if (concomp_cnt < 1 || !once || min_id == -1 || max_id == -1) { return NULL; } // alloc memo for computing leftmost and right most attributes int id_cnt = max_id - min_id + 1; bool *id_exist = new bool[id_cnt]; bool *left_most_exist = new bool[id_cnt]; bool *right_most_exist = new bool[id_cnt]; if (!id_exist || !left_most_exist || !right_most_exist) return NULL; memset(id_exist, 0, id_cnt * sizeof(*id_exist)); memset(left_most_exist, 0, id_cnt * sizeof(*left_most_exist)); memset(right_most_exist, 0, id_cnt * sizeof(*right_most_exist)); // find the dimensions of the charsamp once = false; int left = -1; int right = -1; int top = -1; int bottom = -1; int unq_ids = 0; int unq_left_most = 0; int unq_right_most = 0; for (concomp = strt_concomp; concomp < end_concomp; concomp++) { if (!seg_flags || seg_flags[concomp - strt_concomp] != 0) { if (!once) { left = concomp_array[concomp]->Left(); right = concomp_array[concomp]->Right(); top = concomp_array[concomp]->Top(); bottom = concomp_array[concomp]->Bottom(); once = true; } else { UpdateRange(concomp_array[concomp]->Left(), concomp_array[concomp]->Right(), &left, &right); UpdateRange(concomp_array[concomp]->Top(), concomp_array[concomp]->Bottom(), &top, &bottom); } // count unq ids, unq left most and right mosts ids int concomp_id = concomp_array[concomp]->ID() - min_id; if (!id_exist[concomp_id]) { id_exist[concomp_id] = true; unq_ids++; } if (concomp_array[concomp]->LeftMost()) { if (left_most_exist[concomp_id] == false) { left_most_exist[concomp_id] = true; unq_left_most++; } } if (concomp_array[concomp]->RightMost()) { if (right_most_exist[concomp_id] == false) { right_most_exist[concomp_id] = true; unq_right_most++; } } } } delete []id_exist; delete []left_most_exist; delete []right_most_exist; if (!once || left == -1 || top == -1 || right == -1 || bottom == -1) { return NULL; } (*left_most) = (unq_left_most >= unq_ids); (*right_most) = (unq_right_most >= unq_ids); // create the char sample object CharSamp *samp = new CharSamp(left, top, right - left + 1, bottom - top + 1); if (!samp) { return NULL; } // set the foreground pixels for (concomp = strt_concomp; concomp < end_concomp; concomp++) { if (!seg_flags || seg_flags[concomp - strt_concomp] != 0) { ConCompPt *pt_ptr = concomp_array[concomp]->Head(); while (pt_ptr) { samp->line_buff_[pt_ptr->y() - top][pt_ptr->x() - left] = 0; pt_ptr = pt_ptr->Next(); } } } return samp; } // clones the object CharSamp *CharSamp::Clone() const { // create the cropped char samp CharSamp *samp = new CharSamp(left_, top_, wid_, hgt_); samp->SetLabel(label32_); samp->SetFirstChar(first_char_); samp->SetLastChar(last_char_); samp->SetNormTop(norm_top_); samp->SetNormBottom(norm_bottom_); samp->SetNormAspectRatio(norm_aspect_ratio_); // copy the bitmap to the cropped img Copy(0, 0, wid_, hgt_, samp); return samp; } // Load a Char Samp from a dump file CharSamp *CharSamp::FromCharDumpFile(unsigned char **raw_data_ptr) { unsigned int val32; char_32 *label32; unsigned char *raw_data = *raw_data_ptr; // read and check 32 bit marker memcpy(&val32, raw_data, sizeof(val32)); raw_data += sizeof(val32); if (val32 != 0xabd0fefe) { return NULL; } // read label length, memcpy(&val32, raw_data, sizeof(val32)); raw_data += sizeof(val32); // the label is not null terminated in the file if (val32 > 0 && val32 < MAX_UINT32) { label32 = new char_32[val32 + 1]; if (label32 == NULL) { return NULL; } // read label memcpy(label32, raw_data, val32 * sizeof(*label32)); raw_data += (val32 * sizeof(*label32)); // null terminate label32[val32] = 0; } else { label32 = NULL; } // create the object CharSamp *char_samp = new CharSamp(); if (char_samp == NULL) { return NULL; } // read coordinates char_samp->label32_ = label32; memcpy(&char_samp->page_, raw_data, sizeof(char_samp->page_)); raw_data += sizeof(char_samp->page_); memcpy(&char_samp->left_, raw_data, sizeof(char_samp->left_)); raw_data += sizeof(char_samp->left_); memcpy(&char_samp->top_, raw_data, sizeof(char_samp->top_)); raw_data += sizeof(char_samp->top_); memcpy(&char_samp->first_char_, raw_data, sizeof(char_samp->first_char_)); raw_data += sizeof(char_samp->first_char_); memcpy(&char_samp->last_char_, raw_data, sizeof(char_samp->last_char_)); raw_data += sizeof(char_samp->last_char_); memcpy(&char_samp->norm_top_, raw_data, sizeof(char_samp->norm_top_)); raw_data += sizeof(char_samp->norm_top_); memcpy(&char_samp->norm_bottom_, raw_data, sizeof(char_samp->norm_bottom_)); raw_data += sizeof(char_samp->norm_bottom_); memcpy(&char_samp->norm_aspect_ratio_, raw_data, sizeof(char_samp->norm_aspect_ratio_)); raw_data += sizeof(char_samp->norm_aspect_ratio_); // load the Bmp8 part if (char_samp->LoadFromCharDumpFile(&raw_data) == false) { delete char_samp; return NULL; } (*raw_data_ptr) = raw_data; return char_samp; } // computes the features corresponding to the char sample bool CharSamp::ComputeFeatures(int conv_grid_size, float *features) { // Create a scaled BMP CharSamp *scaled_bmp = Scale(conv_grid_size, conv_grid_size); if (!scaled_bmp) { return false; } // prepare input unsigned char *buff = scaled_bmp->RawData(); // bitmap features int input; int bmp_size = conv_grid_size * conv_grid_size; for (input = 0; input < bmp_size; input++) { features[input] = 255.0f - (1.0f * buff[input]); } // word context features features[input++] = FirstChar(); features[input++] = LastChar(); features[input++] = NormTop(); features[input++] = NormBottom(); features[input++] = NormAspectRatio(); delete scaled_bmp; return true; } } // namespace tesseract tesseract-3.04.01/cube/char_samp.h000066400000000000000000000134231266071204500167060ustar00rootroot00000000000000/********************************************************************** * File: char_samp.h * Description: Declaration of a Character Bitmap Sample Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The CharSamp inherits the Bmp8 class that represents images of // words, characters and segments throughout Cube // CharSamp adds more data members to hold the physical location of the image // in a page, page number in a book if available. // It also holds the label (GT) of the image that might correspond to a single // character or a word // It also provides methods for segmenting, scaling and cropping of the sample #ifndef CHAR_SAMP_H #define CHAR_SAMP_H #include #include #include #include "bmp_8.h" #include "string_32.h" namespace tesseract { class CharSamp : public Bmp8 { public: CharSamp(); CharSamp(int wid, int hgt); CharSamp(int left, int top, int wid, int hgt); ~CharSamp(); // accessor methods unsigned short Left() const { return left_; } unsigned short Right() const { return left_ + wid_; } unsigned short Top() const { return top_; } unsigned short Bottom() const { return top_ + hgt_; } unsigned short Page() const { return page_; } unsigned short NormTop() const { return norm_top_; } unsigned short NormBottom() const { return norm_bottom_; } unsigned short NormAspectRatio() const { return norm_aspect_ratio_; } unsigned short FirstChar() const { return first_char_; } unsigned short LastChar() const { return last_char_; } char_32 Label() const { if (label32_ == NULL || LabelLen() != 1) { return 0; } return label32_[0]; } char_32 * StrLabel() const { return label32_; } string stringLabel() const; void SetLeft(unsigned short left) { left_ = left; } void SetTop(unsigned short top) { top_ = top; } void SetPage(unsigned short page) { page_ = page; } void SetLabel(char_32 label) { if (label32_ != NULL) { delete []label32_; } label32_ = new char_32[2]; if (label32_ != NULL) { label32_[0] = label; label32_[1] = 0; } } void SetLabel(const char_32 *label32) { if (label32_ != NULL) { delete []label32_; label32_ = NULL; } if (label32 != NULL) { // remove any byte order marks if any if (label32[0] == 0xfeff) { label32++; } int len = LabelLen(label32); label32_ = new char_32[len + 1]; if (label32_ != NULL) { memcpy(label32_, label32, len * sizeof(*label32)); label32_[len] = 0; } } } void SetLabel(string str); void SetNormTop(unsigned short norm_top) { norm_top_ = norm_top; } void SetNormBottom(unsigned short norm_bottom) { norm_bottom_ = norm_bottom; } void SetNormAspectRatio(unsigned short norm_aspect_ratio) { norm_aspect_ratio_ = norm_aspect_ratio; } void SetFirstChar(unsigned short first_char) { first_char_ = first_char; } void SetLastChar(unsigned short last_char) { last_char_ = last_char; } // Saves the charsamp to a dump file bool Save2CharDumpFile(FILE *fp) const; // Crops the underlying image and returns a new CharSamp with the // same character information but new dimensions. Warning: does not // necessarily set the normalized top and bottom correctly since // those depend on its location within the word (or CubeSearchObject). CharSamp *Crop(); // Computes the connected components of the char sample ConComp **Segment(int *seg_cnt, bool right_2_left, int max_hist_wnd, int min_con_comp_size) const; // returns a copy of the charsamp that is scaled to the // specified width and height CharSamp *Scale(int wid, int hgt, bool isotropic = true); // returns a Clone of the charsample CharSamp *Clone() const; // computes the features corresponding to the char sample bool ComputeFeatures(int conv_grid_size, float *features); // Load a Char Samp from a dump file static CharSamp *FromCharDumpFile(CachedFile *fp); static CharSamp *FromCharDumpFile(FILE *fp); static CharSamp *FromCharDumpFile(unsigned char **raw_data); static CharSamp *FromRawData(int left, int top, int wid, int hgt, unsigned char *data); static CharSamp *FromConComps(ConComp **concomp_array, int strt_concomp, int seg_flags_size, int *seg_flags, bool *left_most, bool *right_most, int word_hgt); static int AuxFeatureCnt() { return (5); } // Return the length of the label string int LabelLen() const { return LabelLen(label32_); } static int LabelLen(const char_32 *label32) { if (label32 == NULL) { return 0; } int len = 0; while (label32[++len] != 0); return len; } private: char_32 * label32_; unsigned short page_; unsigned short left_; unsigned short top_; // top of sample normalized to a word height of 255 unsigned short norm_top_; // bottom of sample normalized to a word height of 255 unsigned short norm_bottom_; // 255 * ratio of character width to (width + height) unsigned short norm_aspect_ratio_; unsigned short first_char_; unsigned short last_char_; }; } #endif // CHAR_SAMP_H tesseract-3.04.01/cube/char_samp_enum.cpp000066400000000000000000000020201266071204500202540ustar00rootroot00000000000000/********************************************************************** * File: char_samp_enum.cpp * Description: Implementation of a Character Sample Enumerator Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 "char_samp_enum.h" namespace tesseract { CharSampEnum::CharSampEnum() { } CharSampEnum::~CharSampEnum() { } } // namespace ocrlib tesseract-3.04.01/cube/char_samp_enum.h000066400000000000000000000024311266071204500177270ustar00rootroot00000000000000/********************************************************************** * File: char_samp_enum.h * Description: Declaration of a Character Sample Enumerator Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The CharSampEnum class provides the base class for CharSamp class // Enumerators. This is typically used to implement dump file readers #ifndef CHARSAMP_ENUM_H #define CHARSAMP_ENUM_H #include "char_samp.h" namespace tesseract { class CharSampEnum { public: CharSampEnum(); virtual ~CharSampEnum(); virtual bool EnumCharSamp(CharSamp *char_samp, float progress) = 0; }; } #endif // CHARSAMP_ENUM_H tesseract-3.04.01/cube/char_samp_set.cpp000066400000000000000000000110311266071204500201050ustar00rootroot00000000000000/********************************************************************** * File: char_samp_enum.cpp * Description: Implementation of a Character Sample Set Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 #include "char_samp_set.h" #include "cached_file.h" namespace tesseract { CharSampSet::CharSampSet() { cnt_ = 0; samp_buff_ = NULL; own_samples_ = false; } CharSampSet::~CharSampSet() { Cleanup(); } // free buffers and init vars void CharSampSet::Cleanup() { if (samp_buff_ != NULL) { // only free samples if owned by class if (own_samples_ == true) { for (int samp_idx = 0; samp_idx < cnt_; samp_idx++) { if (samp_buff_[samp_idx] != NULL) { delete samp_buff_[samp_idx]; } } } delete []samp_buff_; } cnt_ = 0; samp_buff_ = NULL; } // add a new sample bool CharSampSet::Add(CharSamp *char_samp) { if ((cnt_ % SAMP_ALLOC_BLOCK) == 0) { // create an extended buffer CharSamp **new_samp_buff = reinterpret_cast(new CharSamp *[cnt_ + SAMP_ALLOC_BLOCK]); if (new_samp_buff == NULL) { return false; } // copy old contents if (cnt_ > 0) { memcpy(new_samp_buff, samp_buff_, cnt_ * sizeof(*samp_buff_)); delete []samp_buff_; } samp_buff_ = new_samp_buff; } samp_buff_[cnt_++] = char_samp; return true; } // load char samples from file bool CharSampSet::LoadCharSamples(FILE *fp) { // free existing Cleanup(); // samples are created here and owned by the class own_samples_ = true; // start loading char samples while (feof(fp) == 0) { CharSamp *new_samp = CharSamp::FromCharDumpFile(fp); if (new_samp != NULL) { if (Add(new_samp) == false) { return false; } } } return true; } // creates a CharSampSet object from file CharSampSet * CharSampSet::FromCharDumpFile(string file_name) { FILE *fp; unsigned int val32; // open the file fp = fopen(file_name.c_str(), "rb"); if (fp == NULL) { return NULL; } // read and verify marker if (fread(&val32, 1, sizeof(val32), fp) != sizeof(val32)) { fclose(fp); return NULL; } if (val32 != 0xfefeabd0) { fclose(fp); return NULL; } // create an object CharSampSet *samp_set = new CharSampSet(); if (samp_set == NULL) { fclose(fp); return NULL; } if (samp_set->LoadCharSamples(fp) == false) { delete samp_set; samp_set = NULL; } fclose(fp); return samp_set; } // Create a new Char Dump file FILE *CharSampSet::CreateCharDumpFile(string file_name) { FILE *fp; unsigned int val32; // create the file fp = fopen(file_name.c_str(), "wb"); if (!fp) { return NULL; } // read and verify marker val32 = 0xfefeabd0; if (fwrite(&val32, 1, sizeof(val32), fp) != sizeof(val32)) { fclose(fp); return NULL; } return fp; } // Enumerate the Samples in the set one-by-one calling the enumertor's // EnumCharSamp method for each sample bool CharSampSet::EnumSamples(string file_name, CharSampEnum *enum_obj) { CachedFile *fp_in; unsigned int val32; long i64_size, i64_pos; // open the file fp_in = new CachedFile(file_name); if (fp_in == NULL) { return false; } i64_size = fp_in->Size(); if (i64_size < 1) { return false; } // read and verify marker if (fp_in->Read(&val32, sizeof(val32)) != sizeof(val32)) { return false; } if (val32 != 0xfefeabd0) { return false; } // start loading char samples while (fp_in->eof() == false) { CharSamp *new_samp = CharSamp::FromCharDumpFile(fp_in); i64_pos = fp_in->Tell(); if (new_samp != NULL) { bool ret_flag = (enum_obj)->EnumCharSamp(new_samp, (100.0f * i64_pos / i64_size)); delete new_samp; if (ret_flag == false) { break; } } } delete fp_in; return true; } } // namespace ocrlib tesseract-3.04.01/cube/char_samp_set.h000066400000000000000000000045211266071204500175600ustar00rootroot00000000000000/********************************************************************** * File: char_samp_set.h * Description: Declaration of a Character Sample Set Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The CharSampSet set encapsulates a set of CharSet objects typically // but not necessarily loaded from a file // It provides methods to load samples from File, Create a new file and // Add new char samples to the set #ifndef CHAR_SAMP_SET_H #define CHAR_SAMP_SET_H #include #include #include #include "char_samp.h" #include "char_samp_enum.h" #include "char_set.h" namespace tesseract { // chunks of samp pointers to allocate #define SAMP_ALLOC_BLOCK 10000 class CharSampSet { public: CharSampSet(); ~CharSampSet(); // return sample count int SampleCount() const { return cnt_; } // returns samples buffer CharSamp ** Samples() const { return samp_buff_; } // Create a CharSampSet set object from a file static CharSampSet *FromCharDumpFile(string file_name); // Enumerate the Samples in the set one-by-one calling the enumertor's // EnumCharSamp method for each sample static bool EnumSamples(string file_name, CharSampEnum *enumerator); // Create a new Char Dump file static FILE *CreateCharDumpFile(string file_name); // Add a new sample to the set bool Add(CharSamp *char_samp); private: // sample count int cnt_; // the char samp array CharSamp **samp_buff_; // Are the samples owned by the set or not. // Determines whether we should cleanup in the end bool own_samples_; // Cleanup void Cleanup(); // Load character samples from a file bool LoadCharSamples(FILE *fp); }; } #endif // CHAR_SAMP_SET_H tesseract-3.04.01/cube/char_set.cpp000066400000000000000000000137701266071204500171010ustar00rootroot00000000000000/********************************************************************** * File: char_samp_enum.cpp * Description: Implementation of a Character Set Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 "char_set.h" #include "cube_utils.h" #include "tessdatamanager.h" namespace tesseract { CharSet::CharSet() { class_cnt_ = 0; class_strings_ = NULL; unicharset_map_ = NULL; init_ = false; // init hash table memset(hash_bin_size_, 0, sizeof(hash_bin_size_)); } CharSet::~CharSet() { if (class_strings_ != NULL) { for (int cls = 0; cls < class_cnt_; cls++) { if (class_strings_[cls] != NULL) { delete class_strings_[cls]; } } delete []class_strings_; class_strings_ = NULL; } delete []unicharset_map_; } // Creates CharSet object by reading the unicharset from the // TessDatamanager, and mapping Cube's unicharset to Tesseract's if // they differ. CharSet *CharSet::Create(TessdataManager *tessdata_manager, UNICHARSET *tess_unicharset) { CharSet *char_set = new CharSet(); if (char_set == NULL) { return NULL; } // First look for Cube's unicharset; if not there, use tesseract's bool cube_unicharset_exists; if (!(cube_unicharset_exists = tessdata_manager->SeekToStart(TESSDATA_CUBE_UNICHARSET)) && !tessdata_manager->SeekToStart(TESSDATA_UNICHARSET)) { fprintf(stderr, "Cube ERROR (CharSet::Create): could not find " "either cube or tesseract unicharset\n"); return NULL; } FILE *charset_fp = tessdata_manager->GetDataFilePtr(); if (!charset_fp) { fprintf(stderr, "Cube ERROR (CharSet::Create): could not load " "a unicharset\n"); return NULL; } // If we found a cube unicharset separate from tesseract's, load it and // map its unichars to tesseract's; if only one unicharset exists, // just load it. bool loaded; if (cube_unicharset_exists) { char_set->cube_unicharset_.load_from_file(charset_fp); loaded = tessdata_manager->SeekToStart(TESSDATA_CUBE_UNICHARSET); loaded = loaded && char_set->LoadSupportedCharList( tessdata_manager->GetDataFilePtr(), tess_unicharset); char_set->unicharset_ = &char_set->cube_unicharset_; } else { loaded = char_set->LoadSupportedCharList(charset_fp, NULL); char_set->unicharset_ = tess_unicharset; } if (!loaded) { delete char_set; return NULL; } char_set->init_ = true; return char_set; } // Load the list of supported chars from the given data file pointer. bool CharSet::LoadSupportedCharList(FILE *fp, UNICHARSET *tess_unicharset) { if (init_) return true; char str_line[256]; // init hash table memset(hash_bin_size_, 0, sizeof(hash_bin_size_)); // read the char count if (fgets(str_line, sizeof(str_line), fp) == NULL) { fprintf(stderr, "Cube ERROR (CharSet::InitMemory): could not " "read char count.\n"); return false; } class_cnt_ = atoi(str_line); if (class_cnt_ < 2) { fprintf(stderr, "Cube ERROR (CharSet::InitMemory): invalid " "class count: %d\n", class_cnt_); return false; } // memory for class strings class_strings_ = new string_32*[class_cnt_]; if (class_strings_ == NULL) { fprintf(stderr, "Cube ERROR (CharSet::InitMemory): could not " "allocate memory for class strings.\n"); return false; } // memory for unicharset map if (tess_unicharset) { unicharset_map_ = new int[class_cnt_]; if (unicharset_map_ == NULL) { fprintf(stderr, "Cube ERROR (CharSet::InitMemory): could not " "allocate memory for unicharset map.\n"); return false; } } // Read in character strings and add to hash table for (int class_id = 0; class_id < class_cnt_; class_id++) { // Read the class string if (fgets(str_line, sizeof(str_line), fp) == NULL) { fprintf(stderr, "Cube ERROR (CharSet::ReadAndHashStrings): " "could not read class string with class_id=%d.\n", class_id); return false; } // Terminate at space if any char *p = strchr(str_line, ' '); if (p != NULL) *p = '\0'; // Convert to UTF32 and store string_32 str32; // Convert NULL to a space if (strcmp(str_line, "NULL") == 0) { strcpy(str_line, " "); } CubeUtils::UTF8ToUTF32(str_line, &str32); class_strings_[class_id] = new string_32(str32); if (class_strings_[class_id] == NULL) { fprintf(stderr, "Cube ERROR (CharSet::ReadAndHashStrings): could not " "allocate memory for class string with class_id=%d.\n", class_id); return false; } // Add to hash-table int hash_val = Hash(reinterpret_cast(str32.c_str())); if (hash_bin_size_[hash_val] >= kMaxHashSize) { fprintf(stderr, "Cube ERROR (CharSet::LoadSupportedCharList): hash " "table is full.\n"); return false; } hash_bins_[hash_val][hash_bin_size_[hash_val]++] = class_id; if (tess_unicharset != NULL) { // Add class id to unicharset map UNICHAR_ID tess_id = tess_unicharset->unichar_to_id(str_line); if (tess_id == INVALID_UNICHAR_ID) { tess_unicharset->unichar_insert(str_line); tess_id = tess_unicharset->unichar_to_id(str_line); } ASSERT_HOST(tess_id != INVALID_UNICHAR_ID); unicharset_map_[class_id] = tess_id; } } return true; } } // tesseract tesseract-3.04.01/cube/char_set.h000066400000000000000000000141201266071204500165340ustar00rootroot00000000000000/********************************************************************** * File: char_samp_enum.h * Description: Declaration of a Character Set Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The CharSet class encapsulates the list of 32-bit strings/characters that // Cube supports for a specific language. The char set is loaded from the // .unicharset file corresponding to a specific language // Each string has a corresponding int class-id that gets used throughout Cube // The class provides pass back and forth conversion between the class-id // and its corresponding 32-bit string. This is done using a hash table that // maps the string to the class id. #ifndef CHAR_SET_H #define CHAR_SET_H #include #include #include #include "string_32.h" #include "tessdatamanager.h" #include "unicharset.h" #include "cube_const.h" namespace tesseract { class CharSet { public: CharSet(); ~CharSet(); // Returns true if Cube is sharing Tesseract's unicharset. inline bool SharedUnicharset() { return (unicharset_map_ == NULL); } // Returns the class id corresponding to a 32-bit string. Returns -1 // if the string is not supported. This is done by hashing the // string and then looking up the string in the hash-bin if there // are collisions. inline int ClassID(const char_32 *str) const { int hash_val = Hash(str); if (hash_bin_size_[hash_val] == 0) return -1; for (int bin = 0; bin < hash_bin_size_[hash_val]; bin++) { if (class_strings_[hash_bins_[hash_val][bin]]->compare(str) == 0) return hash_bins_[hash_val][bin]; } return -1; } // Same as above but using a 32-bit char instead of a string inline int ClassID(char_32 ch) const { int hash_val = Hash(ch); if (hash_bin_size_[hash_val] == 0) return -1; for (int bin = 0; bin < hash_bin_size_[hash_val]; bin++) { if ((*class_strings_[hash_bins_[hash_val][bin]])[0] == ch && class_strings_[hash_bins_[hash_val][bin]]->length() == 1) { return hash_bins_[hash_val][bin]; } } return -1; } // Retrieve the unicharid in Tesseract's unicharset corresponding // to a 32-bit string. When Tesseract and Cube share the same // unicharset, this will just be the class id. inline int UnicharID(const char_32 *str) const { int class_id = ClassID(str); if (class_id == INVALID_UNICHAR_ID) return INVALID_UNICHAR_ID; int unichar_id; if (unicharset_map_) unichar_id = unicharset_map_[class_id]; else unichar_id = class_id; return unichar_id; } // Same as above but using a 32-bit char instead of a string inline int UnicharID(char_32 ch) const { int class_id = ClassID(ch); if (class_id == INVALID_UNICHAR_ID) return INVALID_UNICHAR_ID; int unichar_id; if (unicharset_map_) unichar_id = unicharset_map_[class_id]; else unichar_id = class_id; return unichar_id; } // Returns the 32-bit string corresponding to a class id inline const char_32 * ClassString(int class_id) const { if (class_id < 0 || class_id >= class_cnt_) { return NULL; } return reinterpret_cast(class_strings_[class_id]->c_str()); } // Returns the count of supported strings inline int ClassCount() const { return class_cnt_; } // Creates CharSet object by reading the unicharset from the // TessDatamanager, and mapping Cube's unicharset to Tesseract's if // they differ. static CharSet *Create(TessdataManager *tessdata_manager, UNICHARSET *tess_unicharset); // Return the UNICHARSET cube is using for recognition internally -- // ClassId() returns unichar_id's in this unicharset. UNICHARSET *InternalUnicharset() { return unicharset_; } private: // Hash table configuration params. Determined emperically on // the supported languages so far (Eng, Ara, Hin). Might need to be // tuned for speed when more languages are supported static const int kHashBins = 3001; static const int kMaxHashSize = 16; // Using djb2 hashing function to hash a 32-bit string // introduced in http://www.cse.yorku.ca/~oz/hash.html static inline int Hash(const char_32 *str) { unsigned long hash = 5381; int c; while ((c = *str++)) hash = ((hash << 5) + hash) + c; return (hash%kHashBins); } // Same as above but for a single char static inline int Hash(char_32 ch) { char_32 b[2]; b[0] = ch; b[1] = 0; return Hash(b); } // Load the list of supported chars from the given data file // pointer. If tess_unicharset is non-NULL, mapping each Cube class // id to a tesseract unicharid. bool LoadSupportedCharList(FILE *fp, UNICHARSET *tess_unicharset); // class count int class_cnt_; // hash-bin sizes array int hash_bin_size_[kHashBins]; // hash bins int hash_bins_[kHashBins][kMaxHashSize]; // supported strings array string_32 **class_strings_; // map from class id to secondary (tesseract's) unicharset's ids int *unicharset_map_; // A unicharset which is filled in with a Tesseract-style UNICHARSET for // cube's data if our unicharset is different from tesseract's. UNICHARSET cube_unicharset_; // This points to either the tess_unicharset we're passed or cube_unicharset_, // depending upon whether we just have one unicharset or one for each // tesseract and cube, respectively. UNICHARSET *unicharset_; // has the char set been initialized flag bool init_; }; } #endif // CHAR_SET_H tesseract-3.04.01/cube/classifier_base.h000066400000000000000000000061311266071204500200650ustar00rootroot00000000000000/********************************************************************** * File: classifier_base.h * Description: Declaration of the Base Character Classifier * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The CharClassifier class is the abstract class for any character/grapheme // classifier. #ifndef CHAR_CLASSIFIER_BASE_H #define CHAR_CLASSIFIER_BASE_H #include #include "char_samp.h" #include "char_altlist.h" #include "char_set.h" #include "feature_base.h" #include "lang_model.h" #include "tuning_params.h" namespace tesseract { class CharClassifier { public: CharClassifier(CharSet *char_set, TuningParams *params, FeatureBase *feat_extract) { char_set_ = char_set; params_ = params; feat_extract_ = feat_extract; fold_sets_ = NULL; fold_set_cnt_ = 0; fold_set_len_ = NULL; init_ = false; case_sensitive_ = true; } virtual ~CharClassifier() { if (fold_sets_ != NULL) { for (int fold_set = 0; fold_set < fold_set_cnt_; fold_set++) { if (fold_sets_[fold_set] != NULL) { delete []fold_sets_[fold_set]; } } delete []fold_sets_; fold_sets_ = NULL; } if (fold_set_len_ != NULL) { delete []fold_set_len_; fold_set_len_ = NULL; } if (feat_extract_ != NULL) { delete feat_extract_; feat_extract_ = NULL; } } // pure virtual functions that need to be implemented by any inheriting class virtual CharAltList * Classify(CharSamp *char_samp) = 0; virtual int CharCost(CharSamp *char_samp) = 0; virtual bool Train(CharSamp *char_samp, int ClassID) = 0; virtual bool SetLearnParam(char *var_name, float val) = 0; virtual bool Init(const string &data_file_path, const string &lang, LangModel *lang_mod) = 0; // accessors FeatureBase *FeatureExtractor() {return feat_extract_;} inline bool CaseSensitive() const { return case_sensitive_; } inline void SetCaseSensitive(bool case_sensitive) { case_sensitive_ = case_sensitive; } protected: virtual void Fold() = 0; virtual bool LoadFoldingSets(const string &data_file_path, const string &lang, LangModel *lang_mod) = 0; FeatureBase *feat_extract_; CharSet *char_set_; TuningParams *params_; int **fold_sets_; int *fold_set_len_; int fold_set_cnt_; bool init_; bool case_sensitive_; }; } // tesseract #endif // CHAR_CLASSIFIER_BASE_H tesseract-3.04.01/cube/classifier_factory.cpp000066400000000000000000000066351266071204500211660ustar00rootroot00000000000000/********************************************************************** * File: classifier_factory.cpp * Description: Implementation of the Base Character Classifier * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 #include #include "classifier_factory.h" #include "conv_net_classifier.h" #include "feature_base.h" #include "feature_bmp.h" #include "feature_chebyshev.h" #include "feature_hybrid.h" #include "hybrid_neural_net_classifier.h" namespace tesseract { // Creates a CharClassifier object of the appropriate type depending on the // classifier type in the settings file CharClassifier *CharClassifierFactory::Create(const string &data_file_path, const string &lang, LangModel *lang_mod, CharSet *char_set, TuningParams *params) { // create the feature extraction object FeatureBase *feat_extract; switch (params->TypeFeature()) { case TuningParams::BMP: feat_extract = new FeatureBmp(params); break; case TuningParams::CHEBYSHEV: feat_extract = new FeatureChebyshev(params); break; case TuningParams::HYBRID: feat_extract = new FeatureHybrid(params); break; default: fprintf(stderr, "Cube ERROR (CharClassifierFactory::Create): invalid " "feature type.\n"); return NULL; } if (feat_extract == NULL) { fprintf(stderr, "Cube ERROR (CharClassifierFactory::Create): unable " "to instantiate feature extraction object.\n"); return NULL; } // create the classifier object CharClassifier *classifier_obj; switch (params->TypeClassifier()) { case TuningParams::NN: classifier_obj = new ConvNetCharClassifier(char_set, params, feat_extract); break; case TuningParams::HYBRID_NN: classifier_obj = new HybridNeuralNetCharClassifier(char_set, params, feat_extract); break; default: fprintf(stderr, "Cube ERROR (CharClassifierFactory::Create): invalid " "classifier type.\n"); return NULL; } if (classifier_obj == NULL) { fprintf(stderr, "Cube ERROR (CharClassifierFactory::Create): error " "allocating memory for character classifier object.\n"); return NULL; } // Init the classifier if (!classifier_obj->Init(data_file_path, lang, lang_mod)) { delete classifier_obj; fprintf(stderr, "Cube ERROR (CharClassifierFactory::Create): unable " "to Init() character classifier object.\n"); return NULL; } return classifier_obj; } } tesseract-3.04.01/cube/classifier_factory.h000066400000000000000000000031661266071204500206270ustar00rootroot00000000000000/********************************************************************** * File: classifier_factory.h * Description: Declaration of the Base Character Classifier * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The CharClassifierFactory provides a single static method to create an // instance of the desired classifier #ifndef CHAR_CLASSIFIER_FACTORY_H #define CHAR_CLASSIFIER_FACTORY_H #include #include "classifier_base.h" #include "lang_model.h" namespace tesseract { class CharClassifierFactory { public: // Creates a CharClassifier object of the appropriate type depending on the // classifier type in the settings file static CharClassifier *Create(const string &data_file_path, const string &lang, LangModel *lang_mod, CharSet *char_set, TuningParams *params); }; } // tesseract #endif // CHAR_CLASSIFIER_FACTORY_H tesseract-3.04.01/cube/con_comp.cpp000066400000000000000000000153271266071204500171060ustar00rootroot00000000000000/********************************************************************** * File: con_comp.cpp * Description: Implementation of a Connected Component class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 #include "con_comp.h" #include "cube_const.h" namespace tesseract { ConComp::ConComp() { head_ = NULL; tail_ = NULL; left_ = 0; top_ = 0; right_ = 0; bottom_ = 0; left_most_ = false; right_most_ = false; id_ = -1; pt_cnt_ = 0; } ConComp::~ConComp() { if (head_ != NULL) { ConCompPt *pt_ptr = head_; while (pt_ptr != NULL) { ConCompPt *pptNext = pt_ptr->Next(); delete pt_ptr; pt_ptr = pptNext; } head_ = NULL; } } // adds a pt to the conn comp and updates its boundaries bool ConComp::Add(int x, int y) { ConCompPt *pt_ptr = new ConCompPt(x, y); if (pt_ptr == NULL) { return false; } if (head_ == NULL) { left_ = x; right_ = x; top_ = y; bottom_ = y; head_ = pt_ptr; } else { left_ = left_ <= x ? left_ : x; top_ = top_ <= y ? top_ : y; right_ = right_ >= x ? right_ : x; bottom_ = bottom_ >= y ? bottom_ : y; } if (tail_ != NULL) { tail_->SetNext(pt_ptr); } tail_ = pt_ptr; pt_cnt_++; return true; } // merges two connected components bool ConComp::Merge(ConComp *concomp) { if (head_ == NULL || tail_ == NULL || concomp->head_ == NULL || concomp->tail_ == NULL) { return false; } tail_->SetNext(concomp->head_); tail_ = concomp->tail_; left_ = left_ <= concomp->left_ ? left_ : concomp->left_; top_ = top_ <= concomp->top_ ? top_ : concomp->top_; right_ = right_ >= concomp->right_ ? right_ : concomp->right_; bottom_ = bottom_ >= concomp->bottom_ ? bottom_ : concomp->bottom_; pt_cnt_ += concomp->pt_cnt_; concomp->head_ = NULL; concomp->tail_ = NULL; return true; } // Creates the x-coord density histogram after spreading // each x-coord position by the HIST_WND_RATIO fraction of the // height of the ConComp, but limited to max_hist_wnd int *ConComp::CreateHistogram(int max_hist_wnd) { int wid = right_ - left_ + 1, hgt = bottom_ - top_ + 1, hist_wnd = static_cast(hgt * HIST_WND_RATIO); if (hist_wnd > max_hist_wnd) { hist_wnd = max_hist_wnd; } // alloc memo for histogram int *hist_array = new int[wid]; if (hist_array == NULL) { return NULL; } memset(hist_array, 0, wid * sizeof(*hist_array)); // compute windowed histogram ConCompPt *pt_ptr = head_; while (pt_ptr != NULL) { int x = pt_ptr->x() - left_, xw = x - hist_wnd; for (int xdel = -hist_wnd; xdel <= hist_wnd; xdel++, xw++) { if (xw >= 0 && xw < wid) { hist_array[xw]++; } } pt_ptr = pt_ptr->Next(); } return hist_array; } // find out the seg pts by looking for local minima in the histogram int *ConComp::SegmentHistogram(int *hist_array, int *seg_pt_cnt) { // init (*seg_pt_cnt) = 0; int wid = right_ - left_ + 1, hgt = bottom_ - top_ + 1; int *x_seg_pt = new int[wid]; if (x_seg_pt == NULL) { return NULL; } int seg_pt_wnd = static_cast(hgt * SEG_PT_WND_RATIO); if (seg_pt_wnd > 1) { seg_pt_wnd = 1; } for (int x = 2; x < (wid - 2); x++) { if (hist_array[x] < hist_array[x - 1] && hist_array[x] < hist_array[x - 2] && hist_array[x] <= hist_array[x + 1] && hist_array[x] <= hist_array[x + 2]) { x_seg_pt[(*seg_pt_cnt)++] = x; x += seg_pt_wnd; } else if (hist_array[x] <= hist_array[x - 1] && hist_array[x] <= hist_array[x - 2] && hist_array[x] < hist_array[x + 1] && hist_array[x] < hist_array[x + 2]) { x_seg_pt[(*seg_pt_cnt)++] = x; x += seg_pt_wnd; } } // no segments, nothing to do if ((*seg_pt_cnt) == 0) { delete []x_seg_pt; return NULL; } return x_seg_pt; } // segments a concomp based on pixel density histogram local minima // if there were none found, it returns NULL // this is more useful than creating a clone of itself ConComp **ConComp::Segment(int max_hist_wnd, int *concomp_cnt) { // init (*concomp_cnt) = 0; // No pts if (head_ == NULL) { return NULL; } int seg_pt_cnt = 0; // create the histogram int *hist_array = CreateHistogram(max_hist_wnd); if (hist_array == NULL) { return NULL; } int *x_seg_pt = SegmentHistogram(hist_array, &seg_pt_cnt); // free histogram delete []hist_array; // no segments, nothing to do if (seg_pt_cnt == 0) { delete []x_seg_pt; return NULL; } // create concomp array ConComp **concomp_array = new ConComp *[seg_pt_cnt + 1]; if (concomp_array == NULL) { delete []x_seg_pt; return NULL; } for (int concomp = 0; concomp <= seg_pt_cnt; concomp++) { concomp_array[concomp] = new ConComp(); if (concomp_array[concomp] == NULL) { delete []x_seg_pt; delete []concomp_array; return NULL; } // split concomps inherit the ID this concomp concomp_array[concomp]->SetID(id_); } // set the left and right most attributes of the // appropriate concomps concomp_array[0]->left_most_ = true; concomp_array[seg_pt_cnt]->right_most_ = true; // assign pts to concomps ConCompPt *pt_ptr = head_; while (pt_ptr != NULL) { int seg_pt; // find the first seg-pt that exceeds the x value // of the pt for (seg_pt = 0; seg_pt < seg_pt_cnt; seg_pt++) { if ((x_seg_pt[seg_pt] + left_) > pt_ptr->x()) { break; } } // add the pt to the proper concomp if (concomp_array[seg_pt]->Add(pt_ptr->x(), pt_ptr->y()) == false) { delete []x_seg_pt; delete []concomp_array; return NULL; } pt_ptr = pt_ptr->Next(); } delete []x_seg_pt; (*concomp_cnt) = (seg_pt_cnt + 1); return concomp_array; } // Shifts the co-ordinates of all points by the specified x & y deltas void ConComp::Shift(int dx, int dy) { ConCompPt *pt_ptr = head_; while (pt_ptr != NULL) { pt_ptr->Shift(dx, dy); pt_ptr = pt_ptr->Next(); } left_ += dx; right_ += dx; top_ += dy; bottom_ += dy; } } // namespace tesseract tesseract-3.04.01/cube/con_comp.h000066400000000000000000000105201266071204500165410ustar00rootroot00000000000000/********************************************************************** * File: con_comp.h * Description: Declaration of a Connected Component class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 CONCOMP_H #define CONCOMP_H // The ConComp class implements the functionality needed for a // Connected Component object and Connected Component (ConComp) points. // The points consituting a connected component are kept in a linked-list // The Concomp class provided methods to: // 1- Compare components in L2R and R2L reading orders. // 2- Merge ConComps // 3- Compute the windowed vertical pixel density histogram for a specific // windows size // 4- Segment a ConComp based on the local windowed vertical pixel // density histogram local minima namespace tesseract { // Implments a ConComp point in a linked list of points class ConCompPt { public: ConCompPt(int x, int y) { x_ = x; y_ = y; next_pt_ = NULL; } inline int x() { return x_; } inline int y() { return y_; } inline void Shift(int dx, int dy) { x_ += dx; y_ += dy; } inline ConCompPt * Next() { return next_pt_; } inline void SetNext(ConCompPt *pt) { next_pt_ = pt; } private: int x_; int y_; ConCompPt *next_pt_; }; class ConComp { public: ConComp(); virtual ~ConComp(); // accessors inline ConCompPt *Head() { return head_; } inline int Left() const { return left_; } inline int Top() const { return top_; } inline int Right() const { return right_; } inline int Bottom() const { return bottom_; } inline int Width() const { return right_ - left_ + 1; } inline int Height() const { return bottom_ - top_ + 1; } // Comparer used for sorting L2R reading order inline static int Left2RightComparer(const void *comp1, const void *comp2) { return (*(reinterpret_cast(comp1)))->left_ + (*(reinterpret_cast(comp1)))->right_ - (*(reinterpret_cast(comp2)))->left_ - (*(reinterpret_cast(comp2)))->right_; } // Comparer used for sorting R2L reading order inline static int Right2LeftComparer(const void *comp1, const void *comp2) { return (*(reinterpret_cast(comp2)))->right_ - (*(reinterpret_cast(comp1)))->right_; } // accessors for attribues of a ConComp inline bool LeftMost() const { return left_most_; } inline bool RightMost() const { return right_most_; } inline void SetLeftMost(bool left_most) { left_most_ = left_most; } inline void SetRightMost(bool right_most) { right_most_ = right_most; } inline int ID () const { return id_; } inline void SetID(int id) { id_ = id; } inline int PtCnt () const { return pt_cnt_; } // Add a new pt bool Add(int x, int y); // Merge two connected components in-place bool Merge(ConComp *con_comp); // Shifts the co-ordinates of all points by the specified x & y deltas void Shift(int dx, int dy); // segments a concomp based on pixel density histogram local minima ConComp **Segment(int max_hist_wnd, int *concomp_cnt); // creates the vertical pixel density histogram of the concomp int *CreateHistogram(int max_hist_wnd); // find out the seg pts by looking for local minima in the histogram int *SegmentHistogram(int *hist_array, int *seg_pt_cnt); private: int id_; bool left_most_; bool right_most_; int left_; int top_; int right_; int bottom_; ConCompPt *head_; ConCompPt *tail_; int pt_cnt_; }; } #endif // CONCOMP_H tesseract-3.04.01/cube/conv_net_classifier.cpp000066400000000000000000000267501266071204500213320ustar00rootroot00000000000000/********************************************************************** * File: charclassifier.cpp * Description: Implementation of Convolutional-NeuralNet Character Classifier * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 #include #include #include #include #include "char_set.h" #include "classifier_base.h" #include "const.h" #include "conv_net_classifier.h" #include "cube_utils.h" #include "feature_base.h" #include "feature_bmp.h" #include "tess_lang_model.h" namespace tesseract { ConvNetCharClassifier::ConvNetCharClassifier(CharSet *char_set, TuningParams *params, FeatureBase *feat_extract) : CharClassifier(char_set, params, feat_extract) { char_net_ = NULL; net_input_ = NULL; net_output_ = NULL; } ConvNetCharClassifier::~ConvNetCharClassifier() { if (char_net_ != NULL) { delete char_net_; char_net_ = NULL; } if (net_input_ != NULL) { delete []net_input_; net_input_ = NULL; } if (net_output_ != NULL) { delete []net_output_; net_output_ = NULL; } } /** * The main training function. Given a sample and a class ID the classifier * updates its parameters according to its learning algorithm. This function * is currently not implemented. TODO(ahmadab): implement end-2-end training */ bool ConvNetCharClassifier::Train(CharSamp *char_samp, int ClassID) { return false; } /** * A secondary function needed for training. Allows the trainer to set the * value of any train-time parameter. This function is currently not * implemented. TODO(ahmadab): implement end-2-end training */ bool ConvNetCharClassifier::SetLearnParam(char *var_name, float val) { // TODO(ahmadab): implementation of parameter initializing. return false; } /** * Folds the output of the NeuralNet using the loaded folding sets */ void ConvNetCharClassifier::Fold() { // in case insensitive mode if (case_sensitive_ == false) { int class_cnt = char_set_->ClassCount(); // fold case for (int class_id = 0; class_id < class_cnt; class_id++) { // get class string const char_32 *str32 = char_set_->ClassString(class_id); // get the upper case form of the string string_32 upper_form32 = str32; for (int ch = 0; ch < upper_form32.length(); ch++) { if (iswalpha(static_cast(upper_form32[ch])) != 0) { upper_form32[ch] = towupper(upper_form32[ch]); } } // find out the upperform class-id if any int upper_class_id = char_set_->ClassID(reinterpret_cast( upper_form32.c_str())); if (upper_class_id != -1 && class_id != upper_class_id) { float max_out = MAX(net_output_[class_id], net_output_[upper_class_id]); net_output_[class_id] = max_out; net_output_[upper_class_id] = max_out; } } } // The folding sets specify how groups of classes should be folded // Folding involved assigning a min-activation to all the members // of the folding set. The min-activation is a fraction of the max-activation // of the members of the folding set for (int fold_set = 0; fold_set < fold_set_cnt_; fold_set++) { if (fold_set_len_[fold_set] == 0) continue; float max_prob = net_output_[fold_sets_[fold_set][0]]; for (int ch = 1; ch < fold_set_len_[fold_set]; ch++) { if (net_output_[fold_sets_[fold_set][ch]] > max_prob) { max_prob = net_output_[fold_sets_[fold_set][ch]]; } } for (int ch = 0; ch < fold_set_len_[fold_set]; ch++) { net_output_[fold_sets_[fold_set][ch]] = MAX(max_prob * kFoldingRatio, net_output_[fold_sets_[fold_set][ch]]); } } } /** * Compute the features of specified charsamp and feedforward the * specified nets */ bool ConvNetCharClassifier::RunNets(CharSamp *char_samp) { if (char_net_ == NULL) { fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::RunNets): " "NeuralNet is NULL\n"); return false; } int feat_cnt = char_net_->in_cnt(); int class_cnt = char_set_->ClassCount(); // allocate i/p and o/p buffers if needed if (net_input_ == NULL) { net_input_ = new float[feat_cnt]; if (net_input_ == NULL) { fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::RunNets): " "unable to allocate memory for input nodes\n"); return false; } net_output_ = new float[class_cnt]; if (net_output_ == NULL) { fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::RunNets): " "unable to allocate memory for output nodes\n"); return false; } } // compute input features if (feat_extract_->ComputeFeatures(char_samp, net_input_) == false) { fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::RunNets): " "unable to compute features\n"); return false; } if (char_net_ != NULL) { if (char_net_->FeedForward(net_input_, net_output_) == false) { fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::RunNets): " "unable to run feed-forward\n"); return false; } } else { return false; } Fold(); return true; } /** * return the cost of being a char */ int ConvNetCharClassifier::CharCost(CharSamp *char_samp) { if (RunNets(char_samp) == false) { return 0; } return CubeUtils::Prob2Cost(1.0f - net_output_[0]); } /** * classifies a charsamp and returns an alternate list * of chars sorted by char costs */ CharAltList *ConvNetCharClassifier::Classify(CharSamp *char_samp) { // run the needed nets if (RunNets(char_samp) == false) { return NULL; } int class_cnt = char_set_->ClassCount(); // create an altlist CharAltList *alt_list = new CharAltList(char_set_, class_cnt); if (alt_list == NULL) { fprintf(stderr, "Cube WARNING (ConvNetCharClassifier::Classify): " "returning emtpy CharAltList\n"); return NULL; } for (int out = 1; out < class_cnt; out++) { int cost = CubeUtils::Prob2Cost(net_output_[out]); alt_list->Insert(out, cost); } return alt_list; } /** * Set an external net (for training purposes) */ void ConvNetCharClassifier::SetNet(tesseract::NeuralNet *char_net) { if (char_net_ != NULL) { delete char_net_; char_net_ = NULL; } char_net_ = char_net; } /** * This function will return true if the file does not exist. * But will fail if the it did not pass the sanity checks */ bool ConvNetCharClassifier::LoadFoldingSets(const string &data_file_path, const string &lang, LangModel *lang_mod) { fold_set_cnt_ = 0; string fold_file_name; fold_file_name = data_file_path + lang; fold_file_name += ".cube.fold"; // folding sets are optional FILE *fp = fopen(fold_file_name.c_str(), "rb"); if (fp == NULL) { return true; } fclose(fp); string fold_sets_str; if (!CubeUtils::ReadFileToString(fold_file_name, &fold_sets_str)) { return false; } // split into lines vector str_vec; CubeUtils::SplitStringUsing(fold_sets_str, "\r\n", &str_vec); fold_set_cnt_ = str_vec.size(); fold_sets_ = new int *[fold_set_cnt_]; if (fold_sets_ == NULL) { return false; } fold_set_len_ = new int[fold_set_cnt_]; if (fold_set_len_ == NULL) { fold_set_cnt_ = 0; return false; } for (int fold_set = 0; fold_set < fold_set_cnt_; fold_set++) { reinterpret_cast(lang_mod)->RemoveInvalidCharacters( &str_vec[fold_set]); // if all or all but one character are invalid, invalidate this set if (str_vec[fold_set].length() <= 1) { fprintf(stderr, "Cube WARNING (ConvNetCharClassifier::LoadFoldingSets): " "invalidating folding set %d\n", fold_set); fold_set_len_[fold_set] = 0; fold_sets_[fold_set] = NULL; continue; } string_32 str32; CubeUtils::UTF8ToUTF32(str_vec[fold_set].c_str(), &str32); fold_set_len_[fold_set] = str32.length(); fold_sets_[fold_set] = new int[fold_set_len_[fold_set]]; if (fold_sets_[fold_set] == NULL) { fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::LoadFoldingSets): " "could not allocate folding set\n"); fold_set_cnt_ = fold_set; return false; } for (int ch = 0; ch < fold_set_len_[fold_set]; ch++) { fold_sets_[fold_set][ch] = char_set_->ClassID(str32[ch]); } } return true; } /** * Init the classifier provided a data-path and a language string */ bool ConvNetCharClassifier::Init(const string &data_file_path, const string &lang, LangModel *lang_mod) { if (init_) { return true; } // load the nets if any. This function will return true if the net file // does not exist. But will fail if the net did not pass the sanity checks if (!LoadNets(data_file_path, lang)) { return false; } // load the folding sets if any. This function will return true if the // file does not exist. But will fail if the it did not pass the sanity checks if (!LoadFoldingSets(data_file_path, lang, lang_mod)) { return false; } init_ = true; return true; } /** * Load the classifier's Neural Nets * This function will return true if the net file does not exist. * But will fail if the net did not pass the sanity checks */ bool ConvNetCharClassifier::LoadNets(const string &data_file_path, const string &lang) { string char_net_file; // add the lang identifier char_net_file = data_file_path + lang; char_net_file += ".cube.nn"; // neural network is optional FILE *fp = fopen(char_net_file.c_str(), "rb"); if (fp == NULL) { return true; } fclose(fp); // load main net 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()); return false; } // validate net if (char_net_->in_cnt()!= feat_extract_->FeatureCnt()) { fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::LoadNets): " "could not validate net %s\n", char_net_file.c_str()); return false; } // alloc net i/o buffers int feat_cnt = char_net_->in_cnt(); int class_cnt = char_set_->ClassCount(); if (char_net_->out_cnt() != class_cnt) { fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::LoadNets): " "output count (%d) and class count (%d) are not equal\n", char_net_->out_cnt(), class_cnt); return false; } // allocate i/p and o/p buffers if needed if (net_input_ == NULL) { net_input_ = new float[feat_cnt]; if (net_input_ == NULL) { return false; } net_output_ = new float[class_cnt]; if (net_output_ == NULL) { return false; } } return true; } } // tesseract tesseract-3.04.01/cube/conv_net_classifier.h000066400000000000000000000101051266071204500207620ustar00rootroot00000000000000/********************************************************************** * File: conv_net_classifier.h * Description: Declaration of Convolutional-NeuralNet Character Classifier * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The ConvNetCharClassifier inherits from the base classifier class: // "CharClassifierBase". It implements a Convolutional Neural Net classifier // instance of the base classifier. It uses the Tesseract Neural Net library // The Neural Net takes a scaled version of a bitmap and feeds it to a // Convolutional Neural Net as input and performs a FeedForward. Each output // of the net corresponds to class_id in the CharSet passed at construction // time. // Afterwards, the outputs of the Net are "folded" using the folding set // (if any) #ifndef CONV_NET_CLASSIFIER_H #define CONV_NET_CLASSIFIER_H #include #include "char_samp.h" #include "char_altlist.h" #include "char_set.h" #include "feature_base.h" #include "classifier_base.h" #include "neural_net.h" #include "lang_model.h" #include "tuning_params.h" namespace tesseract { // Folding Ratio is the ratio of the max-activation of members of a folding // set that is used to compute the min-activation of the rest of the set static const float kFoldingRatio = 0.75; class ConvNetCharClassifier : public CharClassifier { public: ConvNetCharClassifier(CharSet *char_set, TuningParams *params, FeatureBase *feat_extract); virtual ~ConvNetCharClassifier(); // The main training function. Given a sample and a class ID the classifier // updates its parameters according to its learning algorithm. This function // is currently not implemented. TODO(ahmadab): implement end-2-end training virtual bool Train(CharSamp *char_samp, int ClassID); // A secondary function needed for training. Allows the trainer to set the // value of any train-time parameter. This function is currently not // implemented. TODO(ahmadab): implement end-2-end training virtual bool SetLearnParam(char *var_name, float val); // Externally sets the Neural Net used by the classifier. Used for training void SetNet(tesseract::NeuralNet *net); // Classifies an input charsamp and return a CharAltList object containing // the possible candidates and corresponding scores virtual CharAltList * Classify(CharSamp *char_samp); // Computes the cost of a specific charsamp being a character (versus a // non-character: part-of-a-character OR more-than-one-character) virtual int CharCost(CharSamp *char_samp); private: // Neural Net object used for classification tesseract::NeuralNet *char_net_; // data buffers used to hold Neural Net inputs and outputs float *net_input_; float *net_output_; // Init the classifier provided a data-path and a language string virtual bool Init(const string &data_file_path, const string &lang, LangModel *lang_mod); // Loads the NeuralNets needed for the classifier bool LoadNets(const string &data_file_path, const string &lang); // Loads the folding sets provided a data-path and a language string virtual bool LoadFoldingSets(const string &data_file_path, const string &lang, LangModel *lang_mod); // Folds the output of the NeuralNet using the loaded folding sets virtual void Fold(); // Scales the input char_samp and feeds it to the NeuralNet as input bool RunNets(CharSamp *char_samp); }; } #endif // CONV_NET_CLASSIFIER_H tesseract-3.04.01/cube/cube_const.h000066400000000000000000000026421266071204500170760ustar00rootroot00000000000000/********************************************************************** * File: const.h * Description: Defintions of constants used by Cube * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 CUBE_CONST_H #define CUBE_CONST_H // Scale used to normalize a log-prob to a cost #define PROB2COST_SCALE 4096.0 // Maximum possible cost (-log prob of MIN_PROB) #define MIN_PROB_COST 65536 // Probability corresponding to the max cost MIN_PROB_COST #define MIN_PROB 0.000000113 // Worst possible cost (returned on failure) #define WORST_COST 0x40000 // Oversegmentation hysteresis thresholds #define HIST_WND_RATIO 0.1f #define SEG_PT_WND_RATIO 0.1f #ifdef _WIN32 #ifdef __GNUC__ #include #endif #endif #endif // CUBE_CONST_H tesseract-3.04.01/cube/cube_line_object.cpp000066400000000000000000000221071266071204500205560ustar00rootroot00000000000000/********************************************************************** * File: cube_line_object.cpp * Description: Implementation of the Cube Line Object Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 "cube_line_object.h" namespace tesseract { CubeLineObject::CubeLineObject(CubeRecoContext *cntxt, Pix *pix) { line_pix_ = pix; own_pix_ = false; processed_ = false; cntxt_ = cntxt; phrase_cnt_ = 0; phrases_ = NULL; } CubeLineObject::~CubeLineObject() { if (line_pix_ != NULL && own_pix_ == true) { pixDestroy(&line_pix_); line_pix_ = NULL; } if (phrases_ != NULL) { for (int phrase_idx = 0; phrase_idx < phrase_cnt_; phrase_idx++) { if (phrases_[phrase_idx] != NULL) { delete phrases_[phrase_idx]; } } delete []phrases_; phrases_ = NULL; } } // Recognize the specified pix as one line returning the recognized bool CubeLineObject::Process() { // do nothing if pix had already been processed if (processed_) { return true; } // validate data if (line_pix_ == NULL || cntxt_ == NULL) { return false; } // create a CharSamp CharSamp *char_samp = CubeUtils::CharSampleFromPix(line_pix_, 0, 0, line_pix_->w, line_pix_->h); if (char_samp == NULL) { return false; } // compute connected components. int con_comp_cnt = 0; ConComp **con_comps = char_samp->FindConComps(&con_comp_cnt, cntxt_->Params()->MinConCompSize()); // no longer need char_samp, delete it delete char_samp; // no connected components, bail out if (con_comp_cnt <= 0 || con_comps == NULL) { return false; } // sort connected components based on reading order bool rtl = (cntxt_->ReadingOrder() == tesseract::CubeRecoContext::R2L); qsort(con_comps, con_comp_cnt, sizeof(*con_comps), rtl ? ConComp::Right2LeftComparer : ConComp::Left2RightComparer); // compute work breaking threshold as a ratio of line height bool ret_val = false; int word_break_threshold = ComputeWordBreakThreshold(con_comp_cnt, con_comps, rtl); if (word_break_threshold > 0) { // over-allocate phrases object buffer phrases_ = new CubeObject *[con_comp_cnt]; if (phrases_ != NULL) { // create a phrase if the horizontal distance between two consecutive // concomps is higher than threshold int start_con_idx = 0; int current_phrase_limit = rtl ? con_comps[0]->Left() : con_comps[0]->Right(); for (int con_idx = 1; con_idx <= con_comp_cnt; con_idx++) { bool create_new_phrase = true; // if not at the end, compute the distance between two consecutive // concomps if (con_idx < con_comp_cnt) { int dist = 0; if (cntxt_->ReadingOrder() == tesseract::CubeRecoContext::R2L) { dist = current_phrase_limit - con_comps[con_idx]->Right(); } else { dist = con_comps[con_idx]->Left() - current_phrase_limit; } create_new_phrase = (dist > word_break_threshold); } // create a new phrase if (create_new_phrase) { // create a phrase corresponding to a range on components bool left_most; bool right_most; CharSamp *phrase_char_samp = CharSamp::FromConComps(con_comps, start_con_idx, con_idx - start_con_idx, NULL, &left_most, &right_most, line_pix_->h); if (phrase_char_samp == NULL) { break; } phrases_[phrase_cnt_] = new CubeObject(cntxt_, phrase_char_samp); if (phrases_[phrase_cnt_] == NULL) { delete phrase_char_samp; break; } // set the ownership of the charsamp to the cube object phrases_[phrase_cnt_]->SetCharSampOwnership(true); phrase_cnt_++; // advance the starting index to the current index start_con_idx = con_idx; // set the limit of the newly starting phrase (if any) if (con_idx < con_comp_cnt) { current_phrase_limit = rtl ? con_comps[con_idx]->Left() : con_comps[con_idx]->Right(); } } else { // update the limit of the current phrase if (cntxt_->ReadingOrder() == tesseract::CubeRecoContext::R2L) { current_phrase_limit = MIN(current_phrase_limit, con_comps[con_idx]->Left()); } else { current_phrase_limit = MAX(current_phrase_limit, con_comps[con_idx]->Right()); } } } ret_val = true; } } // clean-up connected comps for (int con_idx = 0; con_idx < con_comp_cnt; con_idx++) { delete con_comps[con_idx]; } delete []con_comps; // success processed_ = true; return ret_val; } // Compute the least word breaking threshold that is required to produce a // valid set of phrases. Phrases are validated using the Aspect ratio // constraints specified in the language specific Params object int CubeLineObject::ComputeWordBreakThreshold(int con_comp_cnt, ConComp **con_comps, bool rtl) { // initial estimate of word breaking threshold int word_break_threshold = static_cast(line_pix_->h * cntxt_->Params()->MaxSpaceHeightRatio()); bool valid = false; // compute the resulting words and validate each's aspect ratio do { // group connected components into words based on breaking threshold int start_con_idx = 0; int current_phrase_limit = (rtl ? con_comps[0]->Left() : con_comps[0]->Right()); int min_x = con_comps[0]->Left(); int max_x = con_comps[0]->Right(); int min_y = con_comps[0]->Top(); int max_y = con_comps[0]->Bottom(); valid = true; for (int con_idx = 1; con_idx <= con_comp_cnt; con_idx++) { bool create_new_phrase = true; // if not at the end, compute the distance between two consecutive // concomps if (con_idx < con_comp_cnt) { int dist = 0; if (rtl) { dist = current_phrase_limit - con_comps[con_idx]->Right(); } else { dist = con_comps[con_idx]->Left() - current_phrase_limit; } create_new_phrase = (dist > word_break_threshold); } // create a new phrase if (create_new_phrase) { // check aspect ratio. Break if invalid if ((max_x - min_x + 1) > (cntxt_->Params()->MaxWordAspectRatio() * (max_y - min_y + 1))) { valid = false; break; } // advance the starting index to the current index start_con_idx = con_idx; // set the limit of the newly starting phrase (if any) if (con_idx < con_comp_cnt) { current_phrase_limit = rtl ? con_comps[con_idx]->Left() : con_comps[con_idx]->Right(); // re-init bounding box min_x = con_comps[con_idx]->Left(); max_x = con_comps[con_idx]->Right(); min_y = con_comps[con_idx]->Top(); max_y = con_comps[con_idx]->Bottom(); } } else { // update the limit of the current phrase if (rtl) { current_phrase_limit = MIN(current_phrase_limit, con_comps[con_idx]->Left()); } else { current_phrase_limit = MAX(current_phrase_limit, con_comps[con_idx]->Right()); } // update bounding box UpdateRange(con_comps[con_idx]->Left(), con_comps[con_idx]->Right(), &min_x, &max_x); UpdateRange(con_comps[con_idx]->Top(), con_comps[con_idx]->Bottom(), &min_y, &max_y); } } // return the breaking threshold if all broken word dimensions are valid if (valid) { return word_break_threshold; } // decrease the threshold and try again word_break_threshold--; } while (!valid && word_break_threshold > 0); // failed to find a threshold that achieves the target aspect ratio. // Just use the default threshold return static_cast(line_pix_->h * cntxt_->Params()->MaxSpaceHeightRatio()); } } tesseract-3.04.01/cube/cube_line_object.h000066400000000000000000000041061266071204500202220ustar00rootroot00000000000000/********************************************************************** * File: cube_line_object.h * Description: Declaration of the Cube Line Object Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The CubeLineObject implements an objects that holds a line of text // Each line is broken into phrases. Phrases are blocks within the line that // are unambiguously separate collections of words #ifndef CUBE_LINE_OBJECT_H #define CUBE_LINE_OBJECT_H #include "cube_reco_context.h" #include "cube_object.h" #include "allheaders.h" namespace tesseract { class CubeLineObject { public: CubeLineObject(CubeRecoContext *cntxt, Pix *pix); ~CubeLineObject(); // accessors inline int PhraseCount() { if (!processed_ && !Process()) { return 0; } return phrase_cnt_; } inline CubeObject **Phrases() { if (!processed_ && !Process()) { return NULL; } return phrases_; } private: CubeRecoContext *cntxt_; bool own_pix_; bool processed_; Pix *line_pix_; CubeObject **phrases_; int phrase_cnt_; bool Process(); // Compute the least word breaking threshold that is required to produce a // valid set of phrases. Phrases are validated using the Aspect ratio // constraints specified in the language specific Params object int ComputeWordBreakThreshold(int con_comp_cnt, ConComp **con_comps, bool rtl); }; } #endif // CUBE_LINE_OBJECT_H tesseract-3.04.01/cube/cube_line_segmenter.cpp000066400000000000000000000622061266071204500213050ustar00rootroot00000000000000/********************************************************************** * File: cube_page_segmenter.cpp * Description: Implementation of the Cube Page Segmenter Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 "cube_line_segmenter.h" #include "ndminx.h" namespace tesseract { // constants that worked for Arabic page segmenter const int CubeLineSegmenter::kLineSepMorphMinHgt = 20; const int CubeLineSegmenter::kHgtBins = 20; const double CubeLineSegmenter::kMaxValidLineRatio = 3.2; const int CubeLineSegmenter::kMaxConnCompHgt = 150; const int CubeLineSegmenter::kMaxConnCompWid = 500; const int CubeLineSegmenter::kMaxHorzAspectRatio = 50; const int CubeLineSegmenter::kMaxVertAspectRatio = 20; const int CubeLineSegmenter::kMinWid = 2; const int CubeLineSegmenter::kMinHgt = 2; const float CubeLineSegmenter::kMinValidLineHgtRatio = 2.5; CubeLineSegmenter::CubeLineSegmenter(CubeRecoContext *cntxt, Pix *img) { cntxt_ = cntxt; orig_img_ = img; img_ = NULL; lines_pixa_ = NULL; init_ = false; line_cnt_ = 0; columns_ = NULL; con_comps_ = NULL; est_alef_hgt_ = 0.0; est_dot_hgt_ = 0.0; } CubeLineSegmenter::~CubeLineSegmenter() { if (img_ != NULL) { pixDestroy(&img_); img_ = NULL; } if (lines_pixa_ != NULL) { pixaDestroy(&lines_pixa_); lines_pixa_ = NULL; } if (con_comps_ != NULL) { pixaDestroy(&con_comps_); con_comps_ = NULL; } if (columns_ != NULL) { pixaaDestroy(&columns_); columns_ = NULL; } } // compute validity ratio for a line double CubeLineSegmenter::ValidityRatio(Pix *line_mask_pix, Box *line_box) { return line_box->h / est_alef_hgt_; } // validate line bool CubeLineSegmenter::ValidLine(Pix *line_mask_pix, Box *line_box) { double validity_ratio = ValidityRatio(line_mask_pix, line_box); return validity_ratio < kMaxValidLineRatio; } // perform a vertical Closing with the specified threshold // returning the resulting conn comps as a pixa Pixa *CubeLineSegmenter::VerticalClosing(Pix *pix, int threshold, Boxa **boxa) { char sequence_str[16]; // do the morphology sprintf(sequence_str, "c100.%d", threshold); Pix *morphed_pix = pixMorphCompSequence(pix, sequence_str, 0); if (morphed_pix == NULL) { return NULL; } // get the resulting lines by computing concomps Pixa *pixac; (*boxa) = pixConnComp(morphed_pix, &pixac, 8); pixDestroy(&morphed_pix); if ((*boxa) == NULL) { return NULL; } return pixac; } // Helper cleans up after CrackLine. static void CleanupCrackLine(int line_cnt, Pixa **lines_pixa, Boxa **line_con_comps, Pixa **line_con_comps_pix) { for (int line = 0; line < line_cnt; line++) { if (lines_pixa[line] != NULL) { pixaDestroy(&lines_pixa[line]); } } delete []lines_pixa; boxaDestroy(line_con_comps); pixaDestroy(line_con_comps_pix); } // do a desperate attempt at cracking lines Pixa *CubeLineSegmenter::CrackLine(Pix *cracked_line_pix, Box *cracked_line_box, int line_cnt) { // create lines pixa array Pixa **lines_pixa = new Pixa*[line_cnt]; if (lines_pixa == NULL) { return NULL; } memset(lines_pixa, 0, line_cnt * sizeof(*lines_pixa)); // compute line conn comps Pixa *line_con_comps_pix; Boxa *line_con_comps = ComputeLineConComps(cracked_line_pix, cracked_line_box, &line_con_comps_pix); if (line_con_comps == NULL) { delete []lines_pixa; return NULL; } // assign each conn comp to the a line based on its centroid for (int con = 0; con < line_con_comps->n; con++) { Box *con_box = line_con_comps->box[con]; Pix *con_pix = line_con_comps_pix->pix[con]; int mid_y = (con_box->y - cracked_line_box->y) + (con_box->h / 2), line_idx = MIN(line_cnt - 1, (mid_y * line_cnt / cracked_line_box->h)); // create the line if it has not been created? if (lines_pixa[line_idx] == NULL) { lines_pixa[line_idx] = pixaCreate(line_con_comps->n); if (lines_pixa[line_idx] == NULL) { CleanupCrackLine(line_cnt, lines_pixa, &line_con_comps, &line_con_comps_pix); return NULL; } } // add the concomp to the line if (pixaAddPix(lines_pixa[line_idx], con_pix, L_CLONE) != 0 || pixaAddBox(lines_pixa[line_idx], con_box, L_CLONE)) { CleanupCrackLine(line_cnt, lines_pixa, &line_con_comps, &line_con_comps_pix); return NULL; } } // create the lines pixa Pixa *lines = pixaCreate(line_cnt); bool success = true; // create and check the validity of the lines for (int line = 0; line < line_cnt; line++) { Pixa *line_pixa = lines_pixa[line]; // skip invalid lines if (line_pixa == NULL) { continue; } // merge the pix, check the validity of the line // and add it to the lines pixa Box *line_box; Pix *line_pix = Pixa2Pix(line_pixa, &line_box); if (line_pix == NULL || line_box == NULL || ValidLine(line_pix, line_box) == false || pixaAddPix(lines, line_pix, L_INSERT) != 0 || pixaAddBox(lines, line_box, L_INSERT) != 0) { if (line_pix != NULL) { pixDestroy(&line_pix); } if (line_box != NULL) { boxDestroy(&line_box); } success = false; break; } } // cleanup CleanupCrackLine(line_cnt, lines_pixa, &line_con_comps, &line_con_comps_pix); if (success == false) { pixaDestroy(&lines); lines = NULL; } return lines; } // do a desperate attempt at cracking lines Pixa *CubeLineSegmenter::CrackLine(Pix *cracked_line_pix, Box *cracked_line_box) { // estimate max line count int max_line_cnt = static_cast((cracked_line_box->h / est_alef_hgt_) + 0.5); if (max_line_cnt < 2) { return NULL; } for (int line_cnt = 2; line_cnt < max_line_cnt; line_cnt++) { Pixa *lines = CrackLine(cracked_line_pix, cracked_line_box, line_cnt); if (lines != NULL) { return lines; } } return NULL; } // split a line continuously until valid or fail Pixa *CubeLineSegmenter::SplitLine(Pix *line_mask_pix, Box *line_box) { // clone the line mask Pix *line_pix = pixClone(line_mask_pix); if (line_pix == NULL) { return NULL; } // AND with the image to get the actual line pixRasterop(line_pix, 0, 0, line_pix->w, line_pix->h, PIX_SRC & PIX_DST, img_, line_box->x, line_box->y); // continue to do rasterop morphology on the line until // it splits to valid lines or we fail int morph_hgt = kLineSepMorphMinHgt - 1, best_threshold = kLineSepMorphMinHgt - 1, max_valid_portion = 0; Boxa *boxa; Pixa *pixac; do { pixac = VerticalClosing(line_pix, morph_hgt, &boxa); // add the box offset to all the lines // and check for the validity of each int line, valid_line_cnt = 0, valid_portion = 0; for (line = 0; line < pixac->n; line++) { boxa->box[line]->x += line_box->x; boxa->box[line]->y += line_box->y; if (ValidLine(pixac->pix[line], boxa->box[line]) == true) { // count valid lines valid_line_cnt++; // and the valid portions valid_portion += boxa->box[line]->h; } } // all the lines are valid if (valid_line_cnt == pixac->n) { boxaDestroy(&boxa); pixDestroy(&line_pix); return pixac; } // a larger valid portion if (valid_portion > max_valid_portion) { max_valid_portion = valid_portion; best_threshold = morph_hgt; } boxaDestroy(&boxa); pixaDestroy(&pixac); morph_hgt--; } while (morph_hgt > 0); // failed to break into valid lines // attempt to crack the line pixac = CrackLine(line_pix, line_box); if (pixac != NULL) { pixDestroy(&line_pix); return pixac; } // try to leverage any of the lines // did the best threshold yield a non zero valid portion if (max_valid_portion > 0) { // use this threshold to break lines pixac = VerticalClosing(line_pix, best_threshold, &boxa); // add the box offset to all the lines // and check for the validity of each for (int line = 0; line < pixac->n; line++) { boxa->box[line]->x += line_box->x; boxa->box[line]->y += line_box->y; // remove invalid lines from the pixa if (ValidLine(pixac->pix[line], boxa->box[line]) == false) { pixaRemovePix(pixac, line); line--; } } boxaDestroy(&boxa); pixDestroy(&line_pix); return pixac; } // last resort: attempt to crack the line pixDestroy(&line_pix); return NULL; } // Checks of a line is too small bool CubeLineSegmenter::SmallLine(Box *line_box) { return line_box->h <= (kMinValidLineHgtRatio * est_dot_hgt_); } // Compute the connected components in a line Boxa * CubeLineSegmenter::ComputeLineConComps(Pix *line_mask_pix, Box *line_box, Pixa **con_comps_pixa) { // clone the line mask Pix *line_pix = pixClone(line_mask_pix); if (line_pix == NULL) { return NULL; } // AND with the image to get the actual line pixRasterop(line_pix, 0, 0, line_pix->w, line_pix->h, PIX_SRC & PIX_DST, img_, line_box->x, line_box->y); // compute the connected components of the line to be merged Boxa *line_con_comps = pixConnComp(line_pix, con_comps_pixa, 8); pixDestroy(&line_pix); // offset boxes by the bbox of the line for (int con = 0; con < line_con_comps->n; con++) { line_con_comps->box[con]->x += line_box->x; line_con_comps->box[con]->y += line_box->y; } return line_con_comps; } // create a union of two arbitrary pix Pix *CubeLineSegmenter::PixUnion(Pix *dest_pix, Box *dest_box, Pix *src_pix, Box *src_box) { // compute dimensions of union rect BOX *union_box = boxBoundingRegion(src_box, dest_box); // create the union pix Pix *union_pix = pixCreate(union_box->w, union_box->h, src_pix->d); if (union_pix == NULL) { return NULL; } // blt the src and dest pix pixRasterop(union_pix, src_box->x - union_box->x, src_box->y - union_box->y, src_box->w, src_box->h, PIX_SRC | PIX_DST, src_pix, 0, 0); pixRasterop(union_pix, dest_box->x - union_box->x, dest_box->y - union_box->y, dest_box->w, dest_box->h, PIX_SRC | PIX_DST, dest_pix, 0, 0); // replace the dest_box *dest_box = *union_box; boxDestroy(&union_box); return union_pix; } // create a union of a number of arbitrary pix Pix *CubeLineSegmenter::Pixa2Pix(Pixa *pixa, Box **dest_box, int start_pix, int pix_cnt) { // compute union_box int min_x = INT_MAX, max_x = INT_MIN, min_y = INT_MAX, max_y = INT_MIN; for (int pix_idx = start_pix; pix_idx < (start_pix + pix_cnt); pix_idx++) { Box *pix_box = pixa->boxa->box[pix_idx]; UpdateRange(pix_box->x, pix_box->x + pix_box->w, &min_x, &max_x); UpdateRange(pix_box->y, pix_box->y + pix_box->h, &min_y, &max_y); } (*dest_box) = boxCreate(min_x, min_y, max_x - min_x, max_y - min_y); if ((*dest_box) == NULL) { 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 NULL; } // create a pix corresponding to the union of all pixs // blt the src and dest pix for (int pix_idx = start_pix; pix_idx < (start_pix + pix_cnt); pix_idx++) { Box *pix_box = pixa->boxa->box[pix_idx]; Pix *con_pix = pixa->pix[pix_idx]; pixRasterop(union_pix, pix_box->x - (*dest_box)->x, pix_box->y - (*dest_box)->y, pix_box->w, pix_box->h, PIX_SRC | PIX_DST, con_pix, 0, 0); } return union_pix; } // create a union of a number of arbitrary pix Pix *CubeLineSegmenter::Pixa2Pix(Pixa *pixa, Box **dest_box) { return Pixa2Pix(pixa, dest_box, 0, pixa->n); } // merges a number of lines into one line given a bounding box and a mask bool CubeLineSegmenter::MergeLine(Pix *line_mask_pix, Box *line_box, Pixa *lines, Boxaa *lines_con_comps) { // compute the connected components of the lines to be merged Pixa *small_con_comps_pix; Boxa *small_line_con_comps = ComputeLineConComps(line_mask_pix, line_box, &small_con_comps_pix); if (small_line_con_comps == NULL) { return false; } // for each connected component for (int con = 0; con < small_line_con_comps->n; con++) { Box *small_con_comp_box = small_line_con_comps->box[con]; int best_line = -1, best_dist = INT_MAX, small_box_right = small_con_comp_box->x + small_con_comp_box->w, small_box_bottom = small_con_comp_box->y + small_con_comp_box->h; // for each valid line for (int line = 0; line < lines->n; line++) { if (SmallLine(lines->boxa->box[line]) == true) { continue; } // for all the connected components in the line Boxa *line_con_comps = lines_con_comps->boxa[line]; for (int lcon = 0; lcon < line_con_comps->n; lcon++) { Box *con_comp_box = line_con_comps->box[lcon]; int xdist, ydist, box_right = con_comp_box->x + con_comp_box->w, box_bottom = con_comp_box->y + con_comp_box->h; xdist = MAX(small_con_comp_box->x, con_comp_box->x) - MIN(small_box_right, box_right); ydist = MAX(small_con_comp_box->y, con_comp_box->y) - MIN(small_box_bottom, box_bottom); // if there is an overlap in x-direction if (xdist <= 0) { if (best_line == -1 || ydist < best_dist) { best_dist = ydist; best_line = line; } } } } // if the distance is too big, do not merged if (best_line != -1 && best_dist < est_alef_hgt_) { // add the pix to the best line Pix *new_line = PixUnion(lines->pix[best_line], lines->boxa->box[best_line], small_con_comps_pix->pix[con], small_con_comp_box); if (new_line == NULL) { return false; } pixDestroy(&lines->pix[best_line]); lines->pix[best_line] = new_line; } } pixaDestroy(&small_con_comps_pix); boxaDestroy(&small_line_con_comps); return true; } // Creates new set of lines from the computed columns bool CubeLineSegmenter::AddLines(Pixa *lines) { // create an array that will hold the bounding boxes // of the concomps belonging to each line Boxaa *lines_con_comps = boxaaCreate(lines->n); if (lines_con_comps == NULL) { return false; } for (int line = 0; line < lines->n; line++) { // if the line is not valid if (ValidLine(lines->pix[line], lines->boxa->box[line]) == false) { // split it Pixa *split_lines = SplitLine(lines->pix[line], lines->boxa->box[line]); // remove the old line if (pixaRemovePix(lines, line) != 0) { return false; } line--; if (split_lines == NULL) { continue; } // add the split lines instead and move the pointer for (int s_line = 0; s_line < split_lines->n; s_line++) { Pix *sp_line = pixaGetPix(split_lines, s_line, L_CLONE); Box *sp_box = boxaGetBox(split_lines->boxa, s_line, L_CLONE); if (sp_line == NULL || sp_box == NULL) { return false; } // insert the new line if (pixaInsertPix(lines, ++line, sp_line, sp_box) != 0) { return false; } } // remove the split lines pixaDestroy(&split_lines); } } // compute the concomps bboxes of each line for (int line = 0; line < lines->n; line++) { Boxa *line_con_comps = ComputeLineConComps(lines->pix[line], lines->boxa->box[line], NULL); if (line_con_comps == NULL) { return false; } // insert it into the boxaa array if (boxaaAddBoxa(lines_con_comps, line_con_comps, L_INSERT) != 0) { return false; } } // post process the lines: // merge the contents of "small" lines info legitimate lines for (int line = 0; line < lines->n; line++) { // a small line detected if (SmallLine(lines->boxa->box[line]) == true) { // merge its components to one of the valid lines if (MergeLine(lines->pix[line], lines->boxa->box[line], lines, lines_con_comps) == true) { // remove the small line if (pixaRemovePix(lines, line) != 0) { return false; } if (boxaaRemoveBoxa(lines_con_comps, line) != 0) { return false; } line--; } } } boxaaDestroy(&lines_con_comps); // add the pix masks if (pixaaAddPixa(columns_, lines, L_INSERT) != 0) { return false; } return true; } // Index the specific pixa using RTL reading order int *CubeLineSegmenter::IndexRTL(Pixa *pixa) { int *pix_index = new int[pixa->n]; if (pix_index == NULL) { return NULL; } for (int pix = 0; pix < pixa->n; pix++) { pix_index[pix] = pix; } for (int ipix = 0; ipix < pixa->n; ipix++) { for (int jpix = ipix + 1; jpix < pixa->n; jpix++) { Box *ipix_box = pixa->boxa->box[pix_index[ipix]], *jpix_box = pixa->boxa->box[pix_index[jpix]]; // swap? if ((ipix_box->x + ipix_box->w) < (jpix_box->x + jpix_box->w)) { int temp = pix_index[ipix]; pix_index[ipix] = pix_index[jpix]; pix_index[jpix] = temp; } } } return pix_index; } // Performs line segmentation bool CubeLineSegmenter::LineSegment() { // Use full image morphology to find columns // This only works for simple layouts where each column // of text extends the full height of the input image. Pix *pix_temp1 = pixMorphCompSequence(img_, "c5.500", 0); if (pix_temp1 == NULL) { return false; } // Mask with a single component over each column Pixa *pixam; Boxa *boxa = pixConnComp(pix_temp1, &pixam, 8); if (boxa == NULL) { return false; } int init_morph_min_hgt = kLineSepMorphMinHgt; char sequence_str[16]; sprintf(sequence_str, "c100.%d", init_morph_min_hgt); // Use selective region-based morphology to get the textline mask. Pixa *pixad = pixaMorphSequenceByRegion(img_, pixam, sequence_str, 0, 0); if (pixad == NULL) { return false; } // for all columns int col_cnt = boxaGetCount(boxa); // create columns columns_ = pixaaCreate(col_cnt); if (columns_ == NULL) { return false; } // index columns based on readind order (RTL) int *col_order = IndexRTL(pixad); if (col_order == NULL) { return false; } line_cnt_ = 0; for (int col_idx = 0; col_idx < col_cnt; col_idx++) { int col = col_order[col_idx]; // get the pix and box corresponding to the column Pix *pixt3 = pixaGetPix(pixad, col, L_CLONE); if (pixt3 == NULL) { delete []col_order; return false; } Box *col_box = pixad->boxa->box[col]; Pixa *pixac; Boxa *boxa2 = pixConnComp(pixt3, &pixac, 8); if (boxa2 == NULL) { delete []col_order; return false; } // offset the boxes by the column box for (int line = 0; line < pixac->n; line++) { pixac->boxa->box[line]->x += col_box->x; pixac->boxa->box[line]->y += col_box->y; } // add the lines if (AddLines(pixac) == true) { if (pixaaAddBox(columns_, col_box, L_CLONE) != 0) { delete []col_order; return false; } } pixDestroy(&pixt3); boxaDestroy(&boxa2); line_cnt_ += columns_->pixa[col_idx]->n; } pixaDestroy(&pixam); pixaDestroy(&pixad); boxaDestroy(&boxa); delete []col_order; pixDestroy(&pix_temp1); return true; } // Estimate the parameters of the font(s) used in the page bool CubeLineSegmenter::EstimateFontParams() { int hgt_hist[kHgtBins]; int max_hgt; double mean_hgt; // init hgt histogram of concomps memset(hgt_hist, 0, sizeof(hgt_hist)); // compute max hgt max_hgt = 0; for (int con = 0; con < con_comps_->n; con++) { // skip conn comps that are too long or too wide if (con_comps_->boxa->box[con]->h > kMaxConnCompHgt || con_comps_->boxa->box[con]->w > kMaxConnCompWid) { continue; } max_hgt = MAX(max_hgt, con_comps_->boxa->box[con]->h); } if (max_hgt <= 0) { return false; } // init hgt histogram of concomps memset(hgt_hist, 0, sizeof(hgt_hist)); // compute histogram mean_hgt = 0.0; for (int con = 0; con < con_comps_->n; con++) { // skip conn comps that are too long or too wide if (con_comps_->boxa->box[con]->h > kMaxConnCompHgt || con_comps_->boxa->box[con]->w > kMaxConnCompWid) { continue; } int bin = static_cast(kHgtBins * con_comps_->boxa->box[con]->h / max_hgt); bin = MIN(bin, kHgtBins - 1); hgt_hist[bin]++; mean_hgt += con_comps_->boxa->box[con]->h; } mean_hgt /= con_comps_->n; // find the top 2 bins int idx[kHgtBins]; for (int bin = 0; bin < kHgtBins; bin++) { idx[bin] = bin; } for (int ibin = 0; ibin < 2; ibin++) { for (int jbin = ibin + 1; jbin < kHgtBins; jbin++) { if (hgt_hist[idx[ibin]] < hgt_hist[idx[jbin]]) { int swap = idx[ibin]; idx[ibin] = idx[jbin]; idx[jbin] = swap; } } } // emperically, we found out that the 2 highest freq bins correspond // respectively to the dot and alef est_dot_hgt_ = (1.0 * (idx[0] + 1) * max_hgt / kHgtBins); est_alef_hgt_ = (1.0 * (idx[1] + 1) * max_hgt / kHgtBins); // as a sanity check the dot hgt must be significanly lower than alef if (est_alef_hgt_ < (est_dot_hgt_ * 2)) { // use max_hgt to estimate instead est_alef_hgt_ = mean_hgt * 1.5; est_dot_hgt_ = est_alef_hgt_ / 5.0; } est_alef_hgt_ = MAX(est_alef_hgt_, est_dot_hgt_ * 4.0); return true; } // clean up the image Pix *CubeLineSegmenter::CleanUp(Pix *orig_img) { // get rid of long horizontal lines Pix *pix_temp0 = pixMorphCompSequence(orig_img, "o300.2", 0); pixXor(pix_temp0, pix_temp0, orig_img); // get rid of long vertical lines Pix *pix_temp1 = pixMorphCompSequence(pix_temp0, "o2.300", 0); pixXor(pix_temp1, pix_temp1, pix_temp0); pixDestroy(&pix_temp0); // detect connected components Pixa *con_comps; Boxa *boxa = pixConnComp(pix_temp1, &con_comps, 8); if (boxa == NULL) { return NULL; } // detect and remove suspicious conn comps for (int con = 0; con < con_comps->n; con++) { Box *box = boxa->box[con]; // remove if suspc. conn comp if ((box->w > (box->h * kMaxHorzAspectRatio)) || (box->h > (box->w * kMaxVertAspectRatio)) || (box->w < kMinWid && box->h < kMinHgt)) { pixRasterop(pix_temp1, box->x, box->y, box->w, box->h, PIX_SRC ^ PIX_DST, con_comps->pix[con], 0, 0); } } pixaDestroy(&con_comps); boxaDestroy(&boxa); return pix_temp1; } // Init the page segmenter bool CubeLineSegmenter::Init() { if (init_ == true) { return true; } if (orig_img_ == NULL) { return false; } // call the internal line segmentation return FindLines(); } // return the pix mask and box of a specific line Pix *CubeLineSegmenter::Line(int line, Box **line_box) { if (init_ == false && Init() == false) { return NULL; } if (line < 0 || line >= line_cnt_) { return NULL; } (*line_box) = lines_pixa_->boxa->box[line]; return lines_pixa_->pix[line]; } // Implements a basic rudimentary layout analysis based on Leptonica // works OK for Arabic. For other languages, the function TesseractPageAnalysis // should be called instead. bool CubeLineSegmenter::FindLines() { // convert the image to gray scale if necessary Pix *gray_scale_img = NULL; if (orig_img_->d != 2 && orig_img_->d != 8) { gray_scale_img = pixConvertTo8(orig_img_, false); if (gray_scale_img == NULL) { return false; } } else { gray_scale_img = orig_img_; } // threshold image Pix *thresholded_img; thresholded_img = pixThresholdToBinary(gray_scale_img, 128); // free the gray scale image if necessary if (gray_scale_img != orig_img_) { pixDestroy(&gray_scale_img); } // bail-out if thresholding failed if (thresholded_img == NULL) { return false; } // deskew Pix *deskew_img = pixDeskew(thresholded_img, 2); if (deskew_img == NULL) { return false; } pixDestroy(&thresholded_img); img_ = CleanUp(deskew_img); pixDestroy(&deskew_img); if (img_ == NULL) { return false; } pixDestroy(&deskew_img); // compute connected components Boxa *boxa = pixConnComp(img_, &con_comps_, 8); if (boxa == NULL) { return false; } boxaDestroy(&boxa); // estimate dot and alef hgts if (EstimateFontParams() == false) { return false; } // perform line segmentation if (LineSegment() == false) { return false; } // success init_ = true; return true; } } tesseract-3.04.01/cube/cube_line_segmenter.h000066400000000000000000000114461266071204500207520ustar00rootroot00000000000000/********************************************************************** * File: cube_page_segmenter.h * Description: Declaration of the Cube Page Segmenter Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // TODO(ahmadab) // This is really a makeshift line segmenter that works well for Arabic // This should eventually be replaced by Ray Smith's Page segmenter // There are lots of magic numbers below that were determined empirically // but not thoroughly tested #ifndef CUBE_LINE_SEGMENTER_H #define CUBE_LINE_SEGMENTER_H #include "cube_reco_context.h" #include "allheaders.h" namespace tesseract { class CubeLineSegmenter { public: CubeLineSegmenter(CubeRecoContext *cntxt, Pix *img); ~CubeLineSegmenter(); // Accessor functions Pix *PostProcessedImage() { if (init_ == false && Init() == false) { return NULL; } return img_; } int ColumnCnt() { if (init_ == false && Init() == false) { return 0; } return columns_->n; } Box *Column(int col) { if (init_ == false && Init() == false) { return NULL; } return columns_->boxa->box[col]; } int LineCnt() { if (init_ == false && Init() == false) { return 0; } return line_cnt_; } Pixa *ConComps() { if (init_ == false && Init() == false) { return NULL; } return con_comps_; } Pixaa *Columns() { if (init_ == false && Init() == false) { return NULL; } return columns_; } inline double AlefHgtEst() { return est_alef_hgt_; } inline double DotHgtEst() { return est_dot_hgt_; } Pix *Line(int line, Box **line_box); private: static const float kMinValidLineHgtRatio; static const int kLineSepMorphMinHgt; static const int kHgtBins; static const int kMaxConnCompHgt; static const int kMaxConnCompWid; static const int kMaxHorzAspectRatio; static const int kMaxVertAspectRatio; static const int kMinWid; static const int kMinHgt; static const double kMaxValidLineRatio; // Cube Reco context CubeRecoContext *cntxt_; // Original image Pix *orig_img_; // Post processed image Pix *img_; // Init flag bool init_; // Output Line and column info int line_cnt_; Pixaa *columns_; Pixa *con_comps_; Pixa *lines_pixa_; // Estimates for sizes of ALEF and DOT needed for Arabic analysis double est_alef_hgt_; double est_dot_hgt_; // Init the page analysis bool Init(); // Performs line segmentation bool LineSegment(); // Cleanup function Pix *CleanUp(Pix *pix); // compute validity ratio for a line double ValidityRatio(Pix *line_mask_pix, Box *line_box); // validate line bool ValidLine(Pix *line_mask_pix, Box *line_box); // split a line continuously until valid or fail Pixa *SplitLine(Pix *line_mask_pix, Box *line_box); // do a desperate attempt at cracking lines Pixa *CrackLine(Pix *line_mask_pix, Box *line_box); Pixa *CrackLine(Pix *line_mask_pix, Box *line_box, int line_cnt); // Checks of a line is too small bool SmallLine(Box *line_box); // Compute the connected components in a line Boxa * ComputeLineConComps(Pix *line_mask_pix, Box *line_box, Pixa **con_comps_pixa); // create a union of two arbitrary pix Pix *PixUnion(Pix *dest_pix, Box *dest_box, Pix *src_pix, Box *src_box); // create a union of a pixa subset Pix *Pixa2Pix(Pixa *pixa, Box **dest_box, int start_pix, int pix_cnt); // create a union of a pixa Pix *Pixa2Pix(Pixa *pixa, Box **dest_box); // merges a number of lines into one line given a bounding box and a mask bool MergeLine(Pix *line_mask_pix, Box *line_box, Pixa *lines, Boxaa *lines_con_comps); // Creates new set of lines from the computed columns bool AddLines(Pixa *lines); // Estimate the parameters of the font(s) used in the page bool EstimateFontParams(); // perform a vertical Closing with the specified threshold // returning the resulting conn comps as a pixa Pixa *VerticalClosing(Pix *pix, int thresold, Boxa **boxa); // Index the specific pixa using RTL reading order int *IndexRTL(Pixa *pixa); // Implements a rudimentary page & line segmenter bool FindLines(); }; } #endif // CUBE_LINE_SEGMENTER_H tesseract-3.04.01/cube/cube_object.cpp000066400000000000000000000205631266071204500175530ustar00rootroot00000000000000/********************************************************************** * File: cube_object.cpp * Description: Implementation of the Cube Object Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 "cube_object.h" #include "cube_utils.h" #include "word_list_lang_model.h" namespace tesseract { CubeObject::CubeObject(CubeRecoContext *cntxt, CharSamp *char_samp) { Init(); char_samp_ = char_samp; cntxt_ = cntxt; } CubeObject::CubeObject(CubeRecoContext *cntxt, Pix *pix, int left, int top, int wid, int hgt) { Init(); char_samp_ = CubeUtils::CharSampleFromPix(pix, left, top, wid, hgt); own_char_samp_ = true; cntxt_ = cntxt; } // Data member initialization function void CubeObject::Init() { char_samp_ = NULL; own_char_samp_ = false; alt_list_ = NULL; srch_obj_ = NULL; deslanted_alt_list_ = NULL; deslanted_srch_obj_ = NULL; deslanted_ = false; deslanted_char_samp_ = NULL; beam_obj_ = NULL; deslanted_beam_obj_ = NULL; cntxt_ = NULL; } // Cleanup function void CubeObject::Cleanup() { if (alt_list_ != NULL) { delete alt_list_; alt_list_ = NULL; } if (deslanted_alt_list_ != NULL) { delete deslanted_alt_list_; deslanted_alt_list_ = NULL; } } CubeObject::~CubeObject() { if (char_samp_ != NULL && own_char_samp_ == true) { delete char_samp_; char_samp_ = NULL; } if (srch_obj_ != NULL) { delete srch_obj_; srch_obj_ = NULL; } if (deslanted_srch_obj_ != NULL) { delete deslanted_srch_obj_; deslanted_srch_obj_ = NULL; } if (beam_obj_ != NULL) { delete beam_obj_; beam_obj_ = NULL; } if (deslanted_beam_obj_ != NULL) { delete deslanted_beam_obj_; deslanted_beam_obj_ = NULL; } if (deslanted_char_samp_ != NULL) { delete deslanted_char_samp_; deslanted_char_samp_ = NULL; } Cleanup(); } /** * Actually do the recognition using the specified language mode. If none * is specified, the default language model in the CubeRecoContext is used. * @return the sorted list of alternate answers * @param word_mode determines whether recognition is done as a word or a phrase */ WordAltList *CubeObject::Recognize(LangModel *lang_mod, bool word_mode) { if (char_samp_ == NULL) { return NULL; } // clear alt lists Cleanup(); // no specified language model, use the one in the reco context if (lang_mod == NULL) { lang_mod = cntxt_->LangMod(); } // normalize if necessary if (cntxt_->SizeNormalization()) { Normalize(); } // assume not de-slanted by default deslanted_ = false; // create a beam search object if (beam_obj_ == NULL) { beam_obj_ = new BeamSearch(cntxt_, word_mode); if (beam_obj_ == NULL) { fprintf(stderr, "Cube ERROR (CubeObject::Recognize): could not construct " "BeamSearch\n"); return NULL; } } // create a cube search object if (srch_obj_ == NULL) { srch_obj_ = new CubeSearchObject(cntxt_, char_samp_); if (srch_obj_ == NULL) { fprintf(stderr, "Cube ERROR (CubeObject::Recognize): could not construct " "CubeSearchObject\n"); return NULL; } } // run a beam search against the tesslang model alt_list_ = beam_obj_->Search(srch_obj_, lang_mod); // deslant (if supported by language) and re-reco if probability is low enough if (cntxt_->HasItalics() == true && (alt_list_ == NULL || alt_list_->AltCount() < 1 || alt_list_->AltCost(0) > CubeUtils::Prob2Cost(kMinProbSkipDeslanted))) { if (deslanted_beam_obj_ == NULL) { deslanted_beam_obj_ = new BeamSearch(cntxt_); if (deslanted_beam_obj_ == NULL) { fprintf(stderr, "Cube ERROR (CubeObject::Recognize): could not " "construct deslanted BeamSearch\n"); return NULL; } } if (deslanted_srch_obj_ == NULL) { deslanted_char_samp_ = char_samp_->Clone(); if (deslanted_char_samp_ == NULL) { fprintf(stderr, "Cube ERROR (CubeObject::Recognize): could not " "construct deslanted CharSamp\n"); return NULL; } if (deslanted_char_samp_->Deslant() == false) { return NULL; } deslanted_srch_obj_ = new CubeSearchObject(cntxt_, deslanted_char_samp_); if (deslanted_srch_obj_ == NULL) { fprintf(stderr, "Cube ERROR (CubeObject::Recognize): could not " "construct deslanted CubeSearchObject\n"); return NULL; } } // run a beam search against the tesslang model deslanted_alt_list_ = deslanted_beam_obj_->Search(deslanted_srch_obj_, lang_mod); // should we use de-slanted altlist? if (deslanted_alt_list_ != NULL && deslanted_alt_list_->AltCount() > 0) { if (alt_list_ == NULL || alt_list_->AltCount() < 1 || deslanted_alt_list_->AltCost(0) < alt_list_->AltCost(0)) { deslanted_ = true; return deslanted_alt_list_; } } } return alt_list_; } /** * Recognize the member char sample as a word */ WordAltList *CubeObject::RecognizeWord(LangModel *lang_mod) { return Recognize(lang_mod, true); } /** * Recognize the member char sample as a phrase */ WordAltList *CubeObject::RecognizePhrase(LangModel *lang_mod) { return Recognize(lang_mod, false); } /** * Computes the cost of a specific string. This is done by performing * recognition of a language model that allows only the specified word */ int CubeObject::WordCost(const char *str) { WordListLangModel *lang_mod = new WordListLangModel(cntxt_); if (lang_mod == NULL) { return WORST_COST; } if (lang_mod->AddString(str) == false) { delete lang_mod; return WORST_COST; } // run a beam search against the single string wordlist model WordAltList *alt_list = RecognizeWord(lang_mod); delete lang_mod; int cost = WORST_COST; if (alt_list != NULL) { if (alt_list->AltCount() > 0) { cost = alt_list->AltCost(0); } } return cost; } // Recognizes a single character and returns the list of results. CharAltList *CubeObject::RecognizeChar() { if (char_samp_ == NULL) return NULL; CharAltList* alt_list = NULL; CharClassifier *char_classifier = cntxt_->Classifier(); ASSERT_HOST(char_classifier != NULL); alt_list = char_classifier->Classify(char_samp_); return alt_list; } // Normalize the input word bitmap to have a minimum aspect ratio bool CubeObject::Normalize() { // create a cube search object CubeSearchObject *srch_obj = new CubeSearchObject(cntxt_, char_samp_); if (srch_obj == NULL) { return false; } // Perform over-segmentation int seg_cnt = srch_obj->SegPtCnt(); // Only perform normalization if segment count is large enough if (seg_cnt < kMinNormalizationSegmentCnt) { delete srch_obj; return true; } // compute the mean AR of the segments double ar_mean = 0.0; for (int seg_idx = 0; seg_idx <= seg_cnt; seg_idx++) { CharSamp *seg_samp = srch_obj->CharSample(seg_idx - 1, seg_idx); if (seg_samp != NULL && seg_samp->Width() > 0) { ar_mean += (1.0 * seg_samp->Height() / seg_samp->Width()); } } ar_mean /= (seg_cnt + 1); // perform normalization if segment AR is too high if (ar_mean > kMinNormalizationAspectRatio) { // scale down the image in the y-direction to attain AR CharSamp *new_samp = char_samp_->Scale(char_samp_->Width(), 2.0 * char_samp_->Height() / ar_mean, false); if (new_samp != NULL) { // free existing char samp if owned if (own_char_samp_) { delete char_samp_; } // update with new scaled charsamp and set ownership flag char_samp_ = new_samp; own_char_samp_ = true; } } delete srch_obj; return true; } } tesseract-3.04.01/cube/cube_object.h000066400000000000000000000155301266071204500172160ustar00rootroot00000000000000/********************************************************************** * File: cube_object.h * Description: Declaration of the Cube Object Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The CubeObject class is the main class used to perform recognition of // a specific char_samp as a single word. // To recognize a word, a CubeObject is constructed for this word. // A Call to RecognizeWord is then issued specifying the language model that // will be used during recognition. If none is specified, the default language // model in the CubeRecoContext is used. The CubeRecoContext is passed at // construction time // // The typical usage pattern for Cube is shown below: // // // Create and initialize Tesseract object and get its // // CubeRecoContext object (note that Tesseract object owns it, // // so it will be freed when the Tesseract object is freed). // tesseract::Tesseract *tess_obj = new tesseract::Tesseract(); // tess_obj->init_tesseract(data_path, lang, tesseract::OEM_CUBE_ONLY); // CubeRecoContext *cntxt = tess_obj->GetCubeRecoContext(); // CHECK(cntxt != NULL) << "Unable to create a Cube reco context"; // . // . // . // // Do this to recognize a word in pix whose co-ordinates are // // (left,top,width,height) // tesseract::CubeObject *cube_obj; // cube_obj = new tesseract::CubeObject(cntxt, pix, // left, top, width, height); // // // Get back Cube's list of answers // tesseract::WordAltList *alt_list = cube_obj->RecognizeWord(); // CHECK(alt_list != NULL && alt_list->AltCount() > 0); // // // Get the string and cost of every alternate // for (int alt = 0; alt < alt_list->AltCount(); alt++) { // // Return the result as a UTF-32 string // string_32 res_str32 = alt_list->Alt(alt); // // Convert to UTF8 if need-be // string res_str; // CubeUtils::UTF32ToUTF8(res_str32.c_str(), &res_str); // // Get the string cost. This should get bigger as you go deeper // // in the list // int cost = alt_list->AltCost(alt); // } // // // Call this once you are done recognizing this word // delete cube_obj; // // // Call this once you are done recognizing all words with // // for the current language // delete tess_obj; // // Note that if the language supports "Italics" (see the CubeRecoContext), the // RecognizeWord function attempts to de-slant the word. #ifndef CUBE_OBJECT_H #define CUBE_OBJECT_H #include "char_samp.h" #include "word_altlist.h" #include "beam_search.h" #include "cube_search_object.h" #include "tess_lang_model.h" #include "cube_reco_context.h" namespace tesseract { // minimum aspect ratio needed to normalize a char_samp before recognition static const float kMinNormalizationAspectRatio = 3.5; // minimum probability a top alt choice must meet before having // deslanted processing applied to it static const float kMinProbSkipDeslanted = 0.25; class CubeObject { public: // Different flavors of constructor. They just differ in the way the // word image is specified CubeObject(CubeRecoContext *cntxt, CharSamp *char_samp); CubeObject(CubeRecoContext *cntxt, Pix *pix, int left, int top, int wid, int hgt); ~CubeObject(); // Perform the word recognition using the specified language mode. If none // is specified, the default language model in the CubeRecoContext is used. // Returns the sorted list of alternate word answers WordAltList *RecognizeWord(LangModel *lang_mod = NULL); // Same as RecognizeWord but recognizes as a phrase WordAltList *RecognizePhrase(LangModel *lang_mod = NULL); // Computes the cost of a specific string. This is done by performing // recognition of a language model that allows only the specified word. // The alternate list(s) will be permanently modified. int WordCost(const char *str); // Recognizes a single character and returns the list of results. CharAltList *RecognizeChar(); // Returns the BeamSearch object that resulted from the last call to // RecognizeWord inline BeamSearch *BeamObj() const { return (deslanted_ == true ? deslanted_beam_obj_ : beam_obj_); } // Returns the WordAltList object that resulted from the last call to // RecognizeWord inline WordAltList *AlternateList() const { return (deslanted_ == true ? deslanted_alt_list_ : alt_list_); } // Returns the CubeSearchObject object that resulted from the last call to // RecognizeWord inline CubeSearchObject *SrchObj() const { return (deslanted_ == true ? deslanted_srch_obj_ : srch_obj_); } // Returns the CharSamp object that resulted from the last call to // RecognizeWord. Note that this object is not necessarily identical to the // one passed at construction time as normalization might have occurred inline CharSamp *CharSample() const { return (deslanted_ == true ? deslanted_char_samp_ : char_samp_); } // Set the ownership of the CharSamp inline void SetCharSampOwnership(bool own_char_samp) { own_char_samp_ = own_char_samp; } protected: // Normalize the CharSamp if its aspect ratio exceeds the below constant. bool Normalize(); private: // minimum segment count needed to normalize a char_samp before recognition static const int kMinNormalizationSegmentCnt = 4; // Data member initialization function void Init(); // Free alternate lists. void Cleanup(); // Perform the actual recognition using the specified language mode. If none // is specified, the default language model in the CubeRecoContext is used. // Returns the sorted list of alternate answers. Called by both // RecognizerWord (word_mode is true) or RecognizePhrase (word mode is false) WordAltList *Recognize(LangModel *lang_mod, bool word_mode); CubeRecoContext *cntxt_; BeamSearch *beam_obj_; BeamSearch *deslanted_beam_obj_; bool own_char_samp_; bool deslanted_; CharSamp *char_samp_; CharSamp *deslanted_char_samp_; CubeSearchObject *srch_obj_; CubeSearchObject *deslanted_srch_obj_; WordAltList *alt_list_; WordAltList *deslanted_alt_list_; }; } #endif // CUBE_OBJECT_H tesseract-3.04.01/cube/cube_search_object.cpp000066400000000000000000000342471266071204500211040ustar00rootroot00000000000000/********************************************************************** * File: cube_search_object.cpp * Description: Implementation of the Cube Search Object Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 "cube_search_object.h" #include "cube_utils.h" #include "ndminx.h" namespace tesseract { const bool CubeSearchObject::kUseCroppedChars = true; CubeSearchObject::CubeSearchObject(CubeRecoContext *cntxt, CharSamp *samp) : SearchObject(cntxt) { init_ = false; reco_cache_ = NULL; samp_cache_ = NULL; segments_ = NULL; segment_cnt_ = 0; samp_ = samp; left_ = 0; itop_ = 0; space_cost_ = NULL; no_space_cost_ = NULL; wid_ = samp_->Width(); hgt_ = samp_->Height(); max_seg_per_char_ = cntxt_->Params()->MaxSegPerChar(); rtl_ = (cntxt_->ReadingOrder() == CubeRecoContext::R2L); min_spc_gap_ = static_cast(hgt_ * cntxt_->Params()->MinSpaceHeightRatio()); max_spc_gap_ = static_cast(hgt_ * cntxt_->Params()->MaxSpaceHeightRatio()); } CubeSearchObject::~CubeSearchObject() { Cleanup(); } // Cleanup void CubeSearchObject::Cleanup() { // delete Recognition Cache if (reco_cache_) { for (int strt_seg = 0; strt_seg < segment_cnt_; strt_seg++) { if (reco_cache_[strt_seg]) { for (int end_seg = 0; end_seg < segment_cnt_; end_seg++) { if (reco_cache_[strt_seg][end_seg]) { delete reco_cache_[strt_seg][end_seg]; } } delete []reco_cache_[strt_seg]; } } delete []reco_cache_; reco_cache_ = NULL; } // delete CharSamp Cache if (samp_cache_) { for (int strt_seg = 0; strt_seg < segment_cnt_; strt_seg++) { if (samp_cache_[strt_seg]) { for (int end_seg = 0; end_seg < segment_cnt_; end_seg++) { if (samp_cache_[strt_seg][end_seg]) { delete samp_cache_[strt_seg][end_seg]; } } delete []samp_cache_[strt_seg]; } } delete []samp_cache_; samp_cache_ = NULL; } // delete segment list if (segments_) { for (int seg = 0; seg < segment_cnt_; seg++) { if (segments_[seg]) { delete segments_[seg]; } } delete []segments_; segments_ = NULL; } if (space_cost_) { delete []space_cost_; space_cost_ = NULL; } if (no_space_cost_) { delete []no_space_cost_; no_space_cost_ = NULL; } segment_cnt_ = 0; init_ = false; } // # of segmentation points. One less than the count of segments int CubeSearchObject::SegPtCnt() { if (!init_ && !Init()) return -1; return segment_cnt_ - 1; } // init and allocate variables, perform segmentation bool CubeSearchObject::Init() { if (init_) return true; if (!Segment()) { return false; } // init cache reco_cache_ = new CharAltList **[segment_cnt_]; if (reco_cache_ == NULL) { fprintf(stderr, "Cube ERROR (CubeSearchObject::Init): could not " "allocate CharAltList array\n"); return false; } samp_cache_ = new CharSamp **[segment_cnt_]; if (samp_cache_ == NULL) { fprintf(stderr, "Cube ERROR (CubeSearchObject::Init): could not " "allocate CharSamp array\n"); return false; } for (int seg = 0; seg < segment_cnt_; seg++) { reco_cache_[seg] = new CharAltList *[segment_cnt_]; if (reco_cache_[seg] == NULL) { fprintf(stderr, "Cube ERROR (CubeSearchObject::Init): could not " "allocate a single segment's CharAltList array\n"); return false; } memset(reco_cache_[seg], 0, segment_cnt_ * sizeof(*reco_cache_[seg])); samp_cache_[seg] = new CharSamp *[segment_cnt_]; if (samp_cache_[seg] == NULL) { fprintf(stderr, "Cube ERROR (CubeSearchObject::Init): could not " "allocate a single segment's CharSamp array\n"); return false; } memset(samp_cache_[seg], 0, segment_cnt_ * sizeof(*samp_cache_[seg])); } init_ = true; return true; } // returns a char sample corresponding to the bitmap between 2 seg pts CharSamp *CubeSearchObject::CharSample(int start_pt, int end_pt) { // init if necessary if (!init_ && !Init()) return NULL; // validate segment range if (!IsValidSegmentRange(start_pt, end_pt)) return NULL; // look for the samp in the cache if (samp_cache_ && samp_cache_[start_pt + 1] && samp_cache_[start_pt + 1][end_pt]) { return samp_cache_[start_pt + 1][end_pt]; } // create a char samp object from the specified range of segments bool left_most; bool right_most; CharSamp *samp = CharSamp::FromConComps(segments_, start_pt + 1, end_pt - start_pt, NULL, &left_most, &right_most, hgt_); if (!samp) return NULL; if (kUseCroppedChars) { CharSamp *cropped_samp = samp->Crop(); // we no longer need the orig sample delete samp; if (!cropped_samp) return NULL; samp = cropped_samp; } // get the dimensions of the new cropped sample int char_top = samp->Top(); int char_wid = samp->Width(); int char_hgt = samp->Height(); // for cursive languages, these features correspond to whether // the charsamp is at the beginning or end of conncomp if (cntxt_->Cursive() == true) { // first and last char flags depend on reading order bool first_char = rtl_ ? right_most : left_most; bool last_char = rtl_ ? left_most : right_most; samp->SetFirstChar(first_char ? 255 : 0); samp->SetLastChar(last_char ? 255 : 0); } else { // for non cursive languages, these features correspond // to whether the charsamp is at the beginning or end of the word samp->SetFirstChar((start_pt == -1) ? 255 : 0); samp->SetLastChar((end_pt == (segment_cnt_ - 1)) ? 255 : 0); } samp->SetNormTop(255 * char_top / hgt_); samp->SetNormBottom(255 * (char_top + char_hgt) / hgt_); samp->SetNormAspectRatio(255 * char_wid / (char_wid + char_hgt)); // add to cache & return samp_cache_[start_pt + 1][end_pt] = samp; return samp; } Box *CubeSearchObject::CharBox(int start_pt, int end_pt) { if (!init_ && !Init()) return NULL; if (!IsValidSegmentRange(start_pt, end_pt)) { fprintf(stderr, "Cube ERROR (CubeSearchObject::CharBox): invalid " "segment range (%d, %d)\n", start_pt, end_pt); return NULL; } // create a char samp object from the specified range of segments, // extract its dimensions into a leptonica box, and delete it bool left_most; bool right_most; CharSamp *samp = CharSamp::FromConComps(segments_, start_pt + 1, end_pt - start_pt, NULL, &left_most, &right_most, hgt_); if (!samp) return NULL; if (kUseCroppedChars) { CharSamp *cropped_samp = samp->Crop(); delete samp; if (!cropped_samp) { return NULL; } samp = cropped_samp; } Box *box = boxCreate(samp->Left(), samp->Top(), samp->Width(), samp->Height()); delete samp; return box; } // call from Beam Search to return the alt list corresponding to // recognizing the bitmap between two segmentation pts CharAltList * CubeSearchObject::RecognizeSegment(int start_pt, int end_pt) { // init if necessary if (!init_ && !Init()) { fprintf(stderr, "Cube ERROR (CubeSearchObject::RecognizeSegment): could " "not initialize CubeSearchObject\n"); return NULL; } // validate segment range if (!IsValidSegmentRange(start_pt, end_pt)) { fprintf(stderr, "Cube ERROR (CubeSearchObject::RecognizeSegment): invalid " "segment range (%d, %d)\n", start_pt, end_pt); return NULL; } // look for the recognition results in cache in the cache if (reco_cache_ && reco_cache_[start_pt + 1] && reco_cache_[start_pt + 1][end_pt]) { return reco_cache_[start_pt + 1][end_pt]; } // create the char sample corresponding to the blob CharSamp *samp = CharSample(start_pt, end_pt); if (!samp) { fprintf(stderr, "Cube ERROR (CubeSearchObject::RecognizeSegment): could " "not construct CharSamp\n"); return NULL; } // recognize the char sample CharClassifier *char_classifier = cntxt_->Classifier(); if (char_classifier) { reco_cache_[start_pt + 1][end_pt] = char_classifier->Classify(samp); } else { // no classifer: all characters are equally probable; add a penalty // that favors 2-segment characters and aspect ratios (w/h) > 1 fprintf(stderr, "Cube WARNING (CubeSearchObject::RecognizeSegment): cube " "context has no character classifier!! Inventing a probability " "distribution.\n"); int class_cnt = cntxt_->CharacterSet()->ClassCount(); CharAltList *alt_list = new CharAltList(cntxt_->CharacterSet(), class_cnt); int seg_cnt = end_pt - start_pt; double prob_val = (1.0 / class_cnt) * exp(-fabs(seg_cnt - 2.0)) * exp(-samp->Width() / static_cast(samp->Height())); if (alt_list) { for (int class_idx = 0; class_idx < class_cnt; class_idx++) { alt_list->Insert(class_idx, CubeUtils::Prob2Cost(prob_val)); } reco_cache_[start_pt + 1][end_pt] = alt_list; } } return reco_cache_[start_pt + 1][end_pt]; } // Perform segmentation of the bitmap by detecting connected components, // segmenting each connected component using windowed vertical pixel density // histogram and sorting the resulting segments in reading order bool CubeSearchObject::Segment() { if (!samp_) return false; segment_cnt_ = 0; segments_ = samp_->Segment(&segment_cnt_, rtl_, cntxt_->Params()->HistWindWid(), cntxt_->Params()->MinConCompSize()); if (!segments_ || segment_cnt_ <= 0) { return false; } if (segment_cnt_ >= kMaxSegmentCnt) { return false; } return true; } // computes the space and no space costs at gaps between segments bool CubeSearchObject::ComputeSpaceCosts() { // init if necessary if (!init_ && !Init()) return false; // Already computed if (space_cost_) return true; // No segmentation points if (segment_cnt_ < 2) return false; // Compute the maximum x to the left of and minimum x to the right of each // segmentation point int *max_left_x = new int[segment_cnt_ - 1]; int *min_right_x = new int[segment_cnt_ - 1]; if (!max_left_x || !min_right_x) { delete []min_right_x; delete []max_left_x; return false; } if (rtl_) { min_right_x[0] = segments_[0]->Left(); max_left_x[segment_cnt_ - 2] = segments_[segment_cnt_ - 1]->Right(); for (int pt_idx = 1; pt_idx < (segment_cnt_ - 1); pt_idx++) { min_right_x[pt_idx] = MIN(min_right_x[pt_idx - 1], segments_[pt_idx]->Left()); max_left_x[segment_cnt_ - pt_idx - 2] = MAX(max_left_x[segment_cnt_ - pt_idx - 1], segments_[segment_cnt_ - pt_idx - 1]->Right()); } } else { min_right_x[segment_cnt_ - 2] = segments_[segment_cnt_ - 1]->Left(); max_left_x[0] = segments_[0]->Right(); for (int pt_idx = 1; pt_idx < (segment_cnt_ - 1); pt_idx++) { min_right_x[segment_cnt_ - pt_idx - 2] = MIN(min_right_x[segment_cnt_ - pt_idx - 1], segments_[segment_cnt_ - pt_idx - 1]->Left()); max_left_x[pt_idx] = MAX(max_left_x[pt_idx - 1], segments_[pt_idx]->Right()); } } // Allocate memory for space and no space costs // trivial cases space_cost_ = new int[segment_cnt_ - 1]; no_space_cost_ = new int[segment_cnt_ - 1]; if (!space_cost_ || !no_space_cost_) { delete []min_right_x; delete []max_left_x; return false; } // go through all segmentation points determining the horizontal gap between // the images on both sides of each break points. Use the gap to estimate // the probability of a space. The probability is modeled a linear function // of the gap width for (int pt_idx = 0; pt_idx < (segment_cnt_ - 1); pt_idx++) { // determine the gap at the segmentation point int gap = min_right_x[pt_idx] - max_left_x[pt_idx]; float prob = 0.0; // gap is too small => no space if (gap < min_spc_gap_) { prob = 0.0; } else if (gap > max_spc_gap_) { // gap is too big => definite space prob = 1.0; } else { // gap is somewhere in between, compute probability prob = (gap - min_spc_gap_) / static_cast(max_spc_gap_ - min_spc_gap_); } // compute cost of space and non-space space_cost_[pt_idx] = CubeUtils::Prob2Cost(prob) + CubeUtils::Prob2Cost(0.1); no_space_cost_[pt_idx] = CubeUtils::Prob2Cost(1.0 - prob); } delete []min_right_x; delete []max_left_x; return true; } // Returns the cost of having a space before the specified segmentation point int CubeSearchObject::SpaceCost(int pt_idx) { if (!space_cost_ && !ComputeSpaceCosts()) { // Failed to compute costs return a zero prob return CubeUtils::Prob2Cost(0.0); } return space_cost_[pt_idx]; } // Returns the cost of not having a space before the specified // segmentation point int CubeSearchObject::NoSpaceCost(int pt_idx) { // If failed to compute costs, return a 1.0 prob if (!space_cost_ && !ComputeSpaceCosts()) return CubeUtils::Prob2Cost(0.0); return no_space_cost_[pt_idx]; } // Returns the cost of not having any spaces within the specified range // of segmentation points int CubeSearchObject::NoSpaceCost(int st_pt, int end_pt) { // If fail to compute costs, return a 1.0 prob if (!space_cost_ && !ComputeSpaceCosts()) return CubeUtils::Prob2Cost(1.0); int no_spc_cost = 0; for (int pt_idx = st_pt + 1; pt_idx < end_pt; pt_idx++) no_spc_cost += NoSpaceCost(pt_idx); return no_spc_cost; } } tesseract-3.04.01/cube/cube_search_object.h000066400000000000000000000110471266071204500205420ustar00rootroot00000000000000/********************************************************************** * File: cube_search_object.h * Description: Declaration of the Cube Search Object Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The CubeSearchObject class represents a char_samp (a word bitmap) that is // being searched for characters (or recognizeable entities). // The Class detects the connected components and peforms an oversegmentation // on each ConComp. The result of which is a list of segments that are ordered // in reading order. // The class provided methods that inquire about the number of segments, the // CharSamp corresponding to any segment range and the recognition results // of any segment range // An object of Class CubeSearchObject is used by the BeamSearch algorithm // to recognize a CharSamp into a list of word alternates #ifndef CUBE_SEARCH_OBJECT_H #define CUBE_SEARCH_OBJECT_H #include "search_object.h" #include "char_samp.h" #include "conv_net_classifier.h" #include "cube_reco_context.h" #include "allheaders.h" namespace tesseract { class CubeSearchObject : public SearchObject { public: CubeSearchObject(CubeRecoContext *cntxt, CharSamp *samp); ~CubeSearchObject(); // returns the Segmentation Point count of the CharSamp owned by the class int SegPtCnt(); // Recognize the set of segments given by the specified range and return // a list of possible alternate answers CharAltList * RecognizeSegment(int start_pt, int end_pt); // Returns the CharSamp corresponding to the specified segment range CharSamp *CharSample(int start_pt, int end_pt); // Returns a leptonica box corresponding to the specified segment range Box *CharBox(int start_pt, int end_pt); // Returns the cost of having a space before the specified segmentation pt int SpaceCost(int seg_pt); // Returns the cost of not having a space before the specified // segmentation pt int NoSpaceCost(int seg_pt); // Returns the cost of not having any spaces within the specified range // of segmentation points int NoSpaceCost(int seg_pt, int end_pt); private: // Maximum reasonable segment count static const int kMaxSegmentCnt = 128; // Use cropped samples static const bool kUseCroppedChars; // reading order flag bool rtl_; // cached dimensions of char samp int left_; int itop_; int wid_; int hgt_; // minimum and maximum and possible inter-segment gaps for spaces int min_spc_gap_; int max_spc_gap_; // initialization flag bool init_; // maximum segments per character: Cached from tuning parameters object int max_seg_per_char_; // char sample to be processed CharSamp *samp_; // segment count int segment_cnt_; // segments of the processed char samp ConComp **segments_; // Cache data members: // There are two caches kept; a CharSamp cache and a CharAltList cache // Each is a 2-D array of CharSamp and CharAltList pointers respectively // hence the triple pointer. CharAltList ***reco_cache_; CharSamp ***samp_cache_; // Cached costs of space and no-space after every segment. Computed only // in phrase mode int *space_cost_; int *no_space_cost_; // init and allocate variables, perform segmentation bool Init(); // Cleanup void Cleanup(); // Perform segmentation of the bitmap by detecting connected components, // segmenting each connected component using windowed vertical pixel density // histogram and sorting the resulting segments in reading order // Returns true on success bool Segment(); // validate the segment ranges. inline bool IsValidSegmentRange(int start_pt, int end_pt) { return (end_pt > start_pt && start_pt >= -1 && start_pt < segment_cnt_ && end_pt >= 0 && end_pt <= segment_cnt_ && end_pt <= (start_pt + max_seg_per_char_)); } // computes the space and no space costs at gaps between segments // return true on success bool ComputeSpaceCosts(); }; } #endif // CUBE_SEARCH_OBJECT_H tesseract-3.04.01/cube/cube_tuning_params.cpp000066400000000000000000000170411266071204500211510ustar00rootroot00000000000000/********************************************************************** * File: cube_tuning_params.cpp * Description: Implementation of the CubeTuningParameters Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 #include "cube_tuning_params.h" #include "tuning_params.h" #include "cube_utils.h" namespace tesseract { CubeTuningParams::CubeTuningParams() { reco_wgt_ = 1.0; size_wgt_ = 1.0; char_bigrams_wgt_ = 1.0; word_unigrams_wgt_ = 0.0; max_seg_per_char_ = 8; beam_width_ = 32; tp_classifier_ = NN; tp_feat_ = BMP; conv_grid_size_ = 32; hist_wind_wid_ = 0; max_word_aspect_ratio_ = 10.0; min_space_height_ratio_ = 0.2; max_space_height_ratio_ = 0.3; min_con_comp_size_ = 0; combiner_run_thresh_ = 1.0; combiner_classifier_thresh_ = 0.5; ood_wgt_ = 1.0; num_wgt_ = 1.0; } CubeTuningParams::~CubeTuningParams() { } // Create an Object given the data file path and the language by loading // the approporiate file CubeTuningParams *CubeTuningParams::Create(const string &data_file_path, const string &lang) { CubeTuningParams *obj = new CubeTuningParams(); if (!obj) { fprintf(stderr, "Cube ERROR (CubeTuningParams::Create): unable to " "allocate new tuning params object\n"); return NULL; } string tuning_params_file; tuning_params_file = data_file_path + lang; tuning_params_file += ".cube.params"; if (!obj->Load(tuning_params_file)) { fprintf(stderr, "Cube ERROR (CubeTuningParams::Create): unable to " "load tuning parameters from %s\n", tuning_params_file.c_str()); delete obj; obj = NULL; } return obj; } // Loads the params file bool CubeTuningParams::Load(string tuning_params_file) { // load the string into memory string param_str; if (CubeUtils::ReadFileToString(tuning_params_file, ¶m_str) == false) { fprintf(stderr, "Cube ERROR (CubeTuningParams::Load): unable to read " "file %s\n", tuning_params_file.c_str()); return false; } // split into lines vector str_vec; CubeUtils::SplitStringUsing(param_str, "\r\n", &str_vec); if (str_vec.size() < 8) { fprintf(stderr, "Cube ERROR (CubeTuningParams::Load): number of rows " "in parameter file is too low\n"); return false; } // for all entries for (int entry = 0; entry < str_vec.size(); entry++) { // tokenize vector str_tok; // should be only two tokens CubeUtils::SplitStringUsing(str_vec[entry], "=", &str_tok); if (str_tok.size() != 2) { fprintf(stderr, "Cube ERROR (CubeTuningParams::Load): invalid format in " "line: %s.\n", str_vec[entry].c_str()); return false; } double val = 0; char peekchar = (str_tok[1].c_str())[0]; if ((peekchar >= '0' && peekchar <= '9') || peekchar == '-' || peekchar == '+' || peekchar == '.') { // read the value if (sscanf(str_tok[1].c_str(), "%lf", &val) != 1) { fprintf(stderr, "Cube ERROR (CubeTuningParams::Load): invalid format " "in line: %s.\n", str_vec[entry].c_str()); return false; } } // token type if (str_tok[0] == "RecoWgt") { reco_wgt_ = val; } else if (str_tok[0] == "SizeWgt") { size_wgt_ = val; } else if (str_tok[0] == "CharBigramsWgt") { char_bigrams_wgt_ = val; } else if (str_tok[0] == "WordUnigramsWgt") { word_unigrams_wgt_ = val; } else if (str_tok[0] == "MaxSegPerChar") { max_seg_per_char_ = static_cast(val); } else if (str_tok[0] == "BeamWidth") { beam_width_ = static_cast(val); } else if (str_tok[0] == "Classifier") { if (str_tok[1] == "NN") { tp_classifier_ = TuningParams::NN; } else if (str_tok[1] == "HYBRID_NN") { tp_classifier_ = TuningParams::HYBRID_NN; } else { fprintf(stderr, "Cube ERROR (CubeTuningParams::Load): invalid " "classifier type in line: %s.\n", str_vec[entry].c_str()); return false; } } else if (str_tok[0] == "FeatureType") { if (str_tok[1] == "BMP") { tp_feat_ = TuningParams::BMP; } else if (str_tok[1] == "CHEBYSHEV") { tp_feat_ = TuningParams::CHEBYSHEV; } else if (str_tok[1] == "HYBRID") { tp_feat_ = TuningParams::HYBRID; } else { fprintf(stderr, "Cube ERROR (CubeTuningParams::Load): invalid feature " "type in line: %s.\n", str_vec[entry].c_str()); return false; } } else if (str_tok[0] == "ConvGridSize") { conv_grid_size_ = static_cast(val); } else if (str_tok[0] == "HistWindWid") { hist_wind_wid_ = val; } else if (str_tok[0] == "MinConCompSize") { min_con_comp_size_ = val; } else if (str_tok[0] == "MaxWordAspectRatio") { max_word_aspect_ratio_ = val; } else if (str_tok[0] == "MinSpaceHeightRatio") { min_space_height_ratio_ = val; } else if (str_tok[0] == "MaxSpaceHeightRatio") { max_space_height_ratio_ = val; } else if (str_tok[0] == "CombinerRunThresh") { combiner_run_thresh_ = val; } else if (str_tok[0] == "CombinerClassifierThresh") { combiner_classifier_thresh_ = val; } else if (str_tok[0] == "OODWgt") { ood_wgt_ = val; } else if (str_tok[0] == "NumWgt") { num_wgt_ = val; } else { fprintf(stderr, "Cube ERROR (CubeTuningParams::Load): unknown parameter " "in line: %s.\n", str_vec[entry].c_str()); return false; } } return true; } // Save the parameters to a file bool CubeTuningParams::Save(string file_name) { FILE *params_file = fopen(file_name.c_str(), "wb"); if (params_file == NULL) { fprintf(stderr, "Cube ERROR (CubeTuningParams::Save): error opening file " "%s for write.\n", file_name.c_str()); return false; } fprintf(params_file, "RecoWgt=%.4f\n", reco_wgt_); fprintf(params_file, "SizeWgt=%.4f\n", size_wgt_); fprintf(params_file, "CharBigramsWgt=%.4f\n", char_bigrams_wgt_); fprintf(params_file, "WordUnigramsWgt=%.4f\n", word_unigrams_wgt_); fprintf(params_file, "MaxSegPerChar=%d\n", max_seg_per_char_); fprintf(params_file, "BeamWidth=%d\n", beam_width_); fprintf(params_file, "ConvGridSize=%d\n", conv_grid_size_); fprintf(params_file, "HistWindWid=%d\n", hist_wind_wid_); fprintf(params_file, "MinConCompSize=%d\n", min_con_comp_size_); fprintf(params_file, "MaxWordAspectRatio=%.4f\n", max_word_aspect_ratio_); fprintf(params_file, "MinSpaceHeightRatio=%.4f\n", min_space_height_ratio_); fprintf(params_file, "MaxSpaceHeightRatio=%.4f\n", max_space_height_ratio_); fprintf(params_file, "CombinerRunThresh=%.4f\n", combiner_run_thresh_); fprintf(params_file, "CombinerClassifierThresh=%.4f\n", combiner_classifier_thresh_); fprintf(params_file, "OODWgt=%.4f\n", ood_wgt_); fprintf(params_file, "NumWgt=%.4f\n", num_wgt_); fclose(params_file); return true; } } tesseract-3.04.01/cube/cube_tuning_params.h000066400000000000000000000036421266071204500206200ustar00rootroot00000000000000/********************************************************************** * File: cube_tuning_params.h * Description: Declaration of the CubeTuningParameters Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The CubeTuningParams class abstracts all the parameters that are used // in Cube and are tuned/learned during the training process. Inherits // from the TuningParams class. #ifndef CUBE_TUNING_PARAMS_H #define CUBE_TUNING_PARAMS_H #include #include "tuning_params.h" namespace tesseract { class CubeTuningParams : public TuningParams { public: CubeTuningParams(); ~CubeTuningParams(); // Accessor functions inline double OODWgt() { return ood_wgt_; } inline double NumWgt() { return num_wgt_; } inline void SetOODWgt(double wgt) { ood_wgt_ = wgt; } inline void SetNumWgt(double wgt) { num_wgt_ = wgt; } // Create an object given the data file path and the language by loading // the approporiate file static CubeTuningParams * Create(const string &data_file, const string &lang); // Save and load the tuning parameters to a specified file bool Save(string file_name); bool Load(string file_name); private: double ood_wgt_; double num_wgt_; }; } #endif // CUBE_TUNING_PARAMS_H tesseract-3.04.01/cube/cube_utils.cpp000077500000000000000000000251311266071204500174440ustar00rootroot00000000000000/********************************************************************** * File: cube_utils.cpp * Description: Implementation of the Cube Utilities Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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 #include #include "cube_utils.h" #include "char_set.h" #include "unichar.h" namespace tesseract { CubeUtils::CubeUtils() { } CubeUtils::~CubeUtils() { } /** * convert a prob to a cost (-ve log prob) */ int CubeUtils::Prob2Cost(double prob_val) { if (prob_val < MIN_PROB) { return MIN_PROB_COST; } return static_cast(-log(prob_val) * PROB2COST_SCALE); } /** * converts a cost to probability */ double CubeUtils::Cost2Prob(int cost) { return exp(-cost / PROB2COST_SCALE); } /** * computes the length of a NULL terminated char_32 string */ int CubeUtils::StrLen(const char_32 *char_32_ptr) { if (char_32_ptr == NULL) { return 0; } int len = -1; while (char_32_ptr[++len]); return len; } /** * compares two char_32 strings */ int CubeUtils::StrCmp(const char_32 *str1, const char_32 *str2) { const char_32 *pch1 = str1; const char_32 *pch2 = str2; for (; (*pch1) != 0 && (*pch2) != 0; pch1++, pch2++) { if ((*pch1) != (*pch2)) { return (*pch1) - (*pch2); } } if ((*pch1) == 0) { if ((*pch2) == 0) { return 0; } else { return -1; } } else { return 1; } } /** * Duplicates a 32-bit char buffer */ char_32 *CubeUtils::StrDup(const char_32 *str32) { int len = StrLen(str32); char_32 *new_str = new char_32[len + 1]; if (new_str == NULL) { return NULL; } memcpy(new_str, str32, len * sizeof(*str32)); new_str[len] = 0; return new_str; } /** * creates a char samp from a specified portion of the image */ CharSamp *CubeUtils::CharSampleFromPix(Pix *pix, int left, int top, int wid, int hgt) { // get the raw img data from the image unsigned char *temp_buff = GetImageData(pix, left, top, wid, hgt); if (temp_buff == NULL) { return NULL; } // create a char samp from temp buffer CharSamp *char_samp = CharSamp::FromRawData(left, top, wid, hgt, temp_buff); // clean up temp buffer delete []temp_buff; return char_samp; } /** * create a B/W image from a char_sample */ Pix *CubeUtils::PixFromCharSample(CharSamp *char_samp) { // parameter check if (char_samp == NULL) { return NULL; } // get the raw data int stride = char_samp->Stride(); int wid = char_samp->Width(); int hgt = char_samp->Height(); Pix *pix = pixCreate(wid, hgt, 1); if (pix == NULL) { return NULL; } // copy the contents unsigned char *line = char_samp->RawData(); for (int y = 0; y < hgt ; y++, line += stride) { for (int x = 0; x < wid; x++) { if (line[x] != 0) { pixSetPixel(pix, x, y, 0); } else { pixSetPixel(pix, x, y, 255); } } } return pix; } /** * creates a raw buffer from the specified location of the pix */ unsigned char *CubeUtils::GetImageData(Pix *pix, int left, int top, int wid, int hgt) { // skip invalid dimensions if (left < 0 || top < 0 || wid < 0 || hgt < 0 || (left + wid) > pix->w || (top + hgt) > pix->h || pix->d != 1) { return NULL; } // copy the char img to a temp buffer unsigned char *temp_buff = new unsigned char[wid * hgt]; if (temp_buff == NULL) { return NULL; } l_int32 w; l_int32 h; l_int32 d; l_int32 wpl; l_uint32 *line; l_uint32 *data; pixGetDimensions(pix, &w, &h, &d); wpl = pixGetWpl(pix); data = pixGetData(pix); line = data + (top * wpl); for (int y = 0, off = 0; y < hgt ; y++) { for (int x = 0; x < wid; x++, off++) { temp_buff[off] = GET_DATA_BIT(line, x + left) ? 0 : 255; } line += wpl; } return temp_buff; } /** * read file contents to a string */ bool CubeUtils::ReadFileToString(const string &file_name, string *str) { str->clear(); FILE *fp = fopen(file_name.c_str(), "rb"); if (fp == NULL) { return false; } // get the size of the size fseek(fp, 0, SEEK_END); int file_size = ftell(fp); if (file_size < 1) { fclose(fp); return false; } // adjust string size str->reserve(file_size); // read the contents rewind(fp); char *buff = new char[file_size]; if (buff == NULL) { fclose(fp); return false; } int read_bytes = fread(buff, 1, static_cast(file_size), fp); if (read_bytes == file_size) { str->append(buff, file_size); } delete []buff; fclose(fp); return (read_bytes == file_size); } /** * splits a string into vectors based on specified delimiters */ void CubeUtils::SplitStringUsing(const string &str, const string &delims, vector *str_vec) { // Optimize the common case where delims is a single character. if (delims[0] != '\0' && delims[1] == '\0') { char c = delims[0]; const char* p = str.data(); const char* end = p + str.size(); while (p != end) { if (*p == c) { ++p; } else { const char* start = p; while (++p != end && *p != c); str_vec->push_back(string(start, p - start)); } } return; } string::size_type begin_index, end_index; begin_index = str.find_first_not_of(delims); while (begin_index != string::npos) { end_index = str.find_first_of(delims, begin_index); if (end_index == string::npos) { str_vec->push_back(str.substr(begin_index)); return; } str_vec->push_back(str.substr(begin_index, (end_index - begin_index))); begin_index = str.find_first_not_of(delims, end_index); } } /** * UTF-8 to UTF-32 conversion functions */ void CubeUtils::UTF8ToUTF32(const char *utf8_str, string_32 *str32) { str32->clear(); 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(); } } } /** * UTF-32 to UTF-8 conversion functions */ void CubeUtils::UTF32ToUTF8(const char_32 *utf32_str, string *str) { str->clear(); for (const char_32 *ch_32 = utf32_str; (*ch_32) != 0; ch_32++) { UNICHAR uni_ch((*ch_32)); char *utf8 = uni_ch.utf8_str(); if (utf8 != NULL) { (*str) += utf8; delete []utf8; } } } bool CubeUtils::IsCaseInvariant(const char_32 *str32, CharSet *char_set) { bool all_one_case = true; bool capitalized; bool prev_upper; bool prev_lower; bool first_upper; bool first_lower; bool cur_upper; bool cur_lower; string str8; if (!char_set) { // If cube char_set is missing, use C-locale-dependent functions // on UTF8 characters to determine case properties. first_upper = isupper(str32[0]); first_lower = islower(str32[0]); if (first_upper) capitalized = true; prev_upper = first_upper; prev_lower = first_lower; for (int c = 1; str32[c] != 0; ++c) { cur_upper = isupper(str32[c]); cur_lower = islower(str32[c]); if ((prev_upper && cur_lower) || (prev_lower && cur_upper)) all_one_case = false; if (cur_upper) capitalized = false; prev_upper = cur_upper; prev_lower = cur_lower; } } else { UNICHARSET *unicharset = char_set->InternalUnicharset(); // Use UNICHARSET functions to determine case properties first_upper = unicharset->get_isupper(char_set->ClassID(str32[0])); first_lower = unicharset->get_islower(char_set->ClassID(str32[0])); if (first_upper) capitalized = true; prev_upper = first_upper; prev_lower = first_lower; for (int c = 1; c < StrLen(str32); ++c) { cur_upper = unicharset->get_isupper(char_set->ClassID(str32[c])); cur_lower = unicharset->get_islower(char_set->ClassID(str32[c])); if ((prev_upper && cur_lower) || (prev_lower && cur_upper)) all_one_case = false; if (cur_upper) capitalized = false; prev_upper = cur_upper; prev_lower = cur_lower; } } return all_one_case || capitalized; } char_32 *CubeUtils::ToLower(const char_32 *str32, CharSet *char_set) { if (!char_set) { return NULL; } UNICHARSET *unicharset = char_set->InternalUnicharset(); int len = StrLen(str32); char_32 *lower = new char_32[len + 1]; if (!lower) return NULL; for (int i = 0; i < len; ++i) { char_32 ch = str32[i]; if (ch == INVALID_UNICHAR_ID) { delete [] lower; return NULL; } // convert upper-case characters to lower-case if (unicharset->get_isupper(char_set->ClassID(ch))) { UNICHAR_ID uid_lower = unicharset->get_other_case(char_set->ClassID(ch)); const char_32 *str32_lower = char_set->ClassString(uid_lower); // expect lower-case version of character to be a single character if (!str32_lower || StrLen(str32_lower) != 1) { delete [] lower; return NULL; } lower[i] = str32_lower[0]; } else { lower[i] = ch; } } lower[len] = 0; return lower; } char_32 *CubeUtils::ToUpper(const char_32 *str32, CharSet *char_set) { if (!char_set) { return NULL; } UNICHARSET *unicharset = char_set->InternalUnicharset(); int len = StrLen(str32); char_32 *upper = new char_32[len + 1]; if (!upper) return NULL; for (int i = 0; i < len; ++i) { char_32 ch = str32[i]; if (ch == INVALID_UNICHAR_ID) { delete [] upper; return NULL; } // convert lower-case characters to upper-case if (unicharset->get_islower(char_set->ClassID(ch))) { UNICHAR_ID uid_upper = unicharset->get_other_case(char_set->ClassID(ch)); const char_32 *str32_upper = char_set->ClassString(uid_upper); // expect upper-case version of character to be a single character if (!str32_upper || StrLen(str32_upper) != 1) { delete [] upper; return NULL; } upper[i] = str32_upper[0]; } else { upper[i] = ch; } } upper[len] = 0; return upper; } } // namespace tesseract tesseract-3.04.01/cube/cube_utils.h000066400000000000000000000070601266071204500171070ustar00rootroot00000000000000/********************************************************************** * File: cube_utils.h * Description: Declaration of the Cube Utilities Class * Author: Ahmad Abdulkader * Created: 2008 * *(C) Copyright 2008, 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. * **********************************************************************/ // The CubeUtils class provides miscellaneous utility and helper functions // to the rest of the Cube Engine #ifndef CUBE_UTILS_H #define CUBE_UTILS_H #include #include #include "allheaders.h" #include "const.h" #include "char_set.h" #include "char_samp.h" namespace tesseract { class CubeUtils { public: CubeUtils(); ~CubeUtils(); // Converts a probability value to a cost by getting the -log() of the // probability value to a known base static int Prob2Cost(double prob_val); // Converts a cost to probability by getting the exp(-normalized cost) static double Cost2Prob(int cost); // Computes the length of a 32-bit char buffer static int StrLen(const char_32 *str); // Compares two 32-bit char buffers static int StrCmp(const char_32 *str1, const char_32 *str2); // Duplicates a 32-bit char buffer static char_32 *StrDup(const char_32 *str); // Creates a CharSamp from an Pix and a bounding box static CharSamp *CharSampleFromPix(Pix *pix, int left, int top, int wid, int hgt); // Creates a Pix from a CharSamp static Pix *PixFromCharSample(CharSamp *char_samp); // read the contents of a file to a string static bool ReadFileToString(const string &file_name, string *str); // split a string into vectors using any of the specified delimiters static void SplitStringUsing(const string &str, const string &delims, vector *str_vec); // UTF-8 to UTF-32 convesion functions static void UTF8ToUTF32(const char *utf8_str, string_32 *str32); static void UTF32ToUTF8(const char_32 *utf32_str, string *str); // Returns true if input word has either 1) all-one-case, or 2) // first character upper-case, and remaining characters lower-case. // If char_set is not NULL, uses tesseract's unicharset functions // to determine case properties. Otherwise, uses C-locale-dependent // functions, which may be unreliable on non-ASCII characters. static bool IsCaseInvariant(const char_32 *str32, CharSet *char_set); // Returns char_32 pointer to the lower-case-transformed version of // the input string or NULL on error. If char_set is NULL returns NULL. // Return array must be freed by caller. static char_32 *ToLower(const char_32 *str32, CharSet *char_set); // Returns char_32 pointer to the upper-case-transformed version of // the input string or NULL on error. If char_set is NULL returns NULL. // Return array must be freed by caller. static char_32 *ToUpper(const char_32 *str32, CharSet *char_set); private: static unsigned char *GetImageData(Pix *pix, int left, int top, int wid, int hgt); }; } // namespace tesseract #endif // CUBE_UTILS_H tesseract-3.04.01/cube/feature_base.h000066400000000000000000000037051266071204500174000ustar00rootroot00000000000000/********************************************************************** * File: feature_base.h * Description: Declaration of the Feature Base Class * Author: Ping Ping (xiupingping), Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The FeatureBase class is the base class for any Feature Extraction class // It provided 3 pure virtual functions (to inherit): // 1- FeatureCnt: A method to returns the count of features // 2- ComputeFeatures: A method to compute the features for a given CharSamp // 3- ComputeFeatureBitmap: A method to render a visualization of the features // to a CharSamp. This is mainly used by visual-debuggers #ifndef FEATURE_BASE_H #define FEATURE_BASE_H #include "char_samp.h" #include "tuning_params.h" namespace tesseract { class FeatureBase { public: explicit FeatureBase(TuningParams *params) : params_(params) { } virtual ~FeatureBase() {} // Compute the features for a given CharSamp virtual bool ComputeFeatures(CharSamp *char_samp, float *features) = 0; // Render a visualization of the features to a CharSamp. // This is mainly used by visual-debuggers virtual CharSamp *ComputeFeatureBitmap(CharSamp *char_samp) = 0; // Returns the count of features virtual int FeatureCnt() = 0; protected: TuningParams *params_; }; } #endif // FEATURE_BASE_H tesseract-3.04.01/cube/feature_bmp.cpp000066400000000000000000000031571266071204500176000ustar00rootroot00000000000000/********************************************************************** * File: feature_bmp.cpp * Description: Implementation of the Bitmap Feature Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 #include #include "feature_base.h" #include "feature_bmp.h" #include "cube_utils.h" #include "const.h" #include "char_samp.h" namespace tesseract { FeatureBmp::FeatureBmp(TuningParams *params) :FeatureBase(params) { conv_grid_size_ = params->ConvGridSize(); } FeatureBmp::~FeatureBmp() { } // Render a visualization of the features to a CharSamp. // This is mainly used by visual-debuggers CharSamp *FeatureBmp::ComputeFeatureBitmap(CharSamp *char_samp) { return char_samp->Scale(conv_grid_size_, conv_grid_size_); } // Compute the features for a given CharSamp bool FeatureBmp::ComputeFeatures(CharSamp *char_samp, float *features) { return char_samp->ComputeFeatures(conv_grid_size_, features); } } tesseract-3.04.01/cube/feature_bmp.h000066400000000000000000000036761266071204500172530ustar00rootroot00000000000000/********************************************************************** * File: feature_bmp.h * Description: Declaration of the Bitmap Feature Class * Author: PingPing xiu (xiupingping) & Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The FeatureBmp class implements a Bitmap feature extractor class. It // inherits from the FeatureBase class // The Bitmap feature vectors is the the bitmap of the specified CharSamp // scaled to a fixed grid size and then augmented by a 5 aux features that // describe the size, aspect ration and placement within a word #ifndef FEATURE_BMP_H #define FEATURE_BMP_H #include "char_samp.h" #include "feature_base.h" namespace tesseract { class FeatureBmp : public FeatureBase { public: explicit FeatureBmp(TuningParams *params); virtual ~FeatureBmp(); // Render a visualization of the features to a CharSamp. // This is mainly used by visual-debuggers virtual CharSamp *ComputeFeatureBitmap(CharSamp *samp); // Compute the features for a given CharSamp virtual bool ComputeFeatures(CharSamp *samp, float *features); // Returns the count of features virtual int FeatureCnt() { return 5 + (conv_grid_size_ * conv_grid_size_); } protected: // grid size, cached from the TuningParams object int conv_grid_size_; }; } #endif // FEATURE_BMP_H tesseract-3.04.01/cube/feature_chebyshev.cpp000066400000000000000000000121721266071204500207770ustar00rootroot00000000000000/********************************************************************** * File: feature_chebyshev.cpp * Description: Implementation of the Chebyshev coefficients Feature Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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 #include #include #include #include #include "feature_base.h" #include "feature_chebyshev.h" #include "cube_utils.h" #include "const.h" #include "char_samp.h" namespace tesseract { FeatureChebyshev::FeatureChebyshev(TuningParams *params) : FeatureBase(params) { } FeatureChebyshev::~FeatureChebyshev() { } // Render a visualization of the features to a CharSamp. // This is mainly used by visual-debuggers CharSamp *FeatureChebyshev::ComputeFeatureBitmap(CharSamp *char_samp) { return char_samp; } // Compute Chebyshev coefficients for the specified vector void FeatureChebyshev::ChebyshevCoefficients(const vector &input, int coeff_cnt, float *coeff) { // re-sample function int input_range = (input.size() - 1); vector resamp(coeff_cnt); for (int samp_idx = 0; samp_idx < coeff_cnt; samp_idx++) { // compute sampling position float samp_pos = input_range * (1 + cos(M_PI * (samp_idx + 0.5) / coeff_cnt)) / 2; // interpolate int samp_start = static_cast(samp_pos); int samp_end = static_cast(samp_pos + 0.5); float func_delta = input[samp_end] - input[samp_start]; resamp[samp_idx] = input[samp_start] + ((samp_pos - samp_start) * func_delta); } // compute the coefficients float normalizer = 2.0 / coeff_cnt; for (int coeff_idx = 0; coeff_idx < coeff_cnt; coeff_idx++, coeff++) { double sum = 0.0; for (int samp_idx = 0; samp_idx < coeff_cnt; samp_idx++) { sum += resamp[samp_idx] * cos(M_PI * coeff_idx * (samp_idx + 0.5) / coeff_cnt); } (*coeff) = (normalizer * sum); } } // Compute the features of a given CharSamp bool FeatureChebyshev::ComputeFeatures(CharSamp *char_samp, float *features) { return ComputeChebyshevCoefficients(char_samp, features); } // Compute the Chebyshev coefficients of a given CharSamp bool FeatureChebyshev::ComputeChebyshevCoefficients(CharSamp *char_samp, float *features) { if (char_samp->NormBottom() <= 0) { return false; } unsigned char *raw_data = char_samp->RawData(); int stride = char_samp->Stride(); // compute the height of the word int word_hgt = (255 * (char_samp->Top() + char_samp->Height()) / char_samp->NormBottom()); // compute left & right profiles vector left_profile(word_hgt, 0.0); vector right_profile(word_hgt, 0.0); unsigned char *line_data = raw_data; for (int y = 0; y < char_samp->Height(); y++, line_data += stride) { int min_x = char_samp->Width(); int max_x = -1; for (int x = 0; x < char_samp->Width(); x++) { if (line_data[x] == 0) { UpdateRange(x, &min_x, &max_x); } } left_profile[char_samp->Top() + y] = 1.0 * (min_x == char_samp->Width() ? 0 : (min_x + 1)) / char_samp->Width(); right_profile[char_samp->Top() + y] = 1.0 * (max_x == -1 ? 0 : char_samp->Width() - max_x) / char_samp->Width(); } // compute top and bottom profiles vector top_profile(char_samp->Width(), 0); vector bottom_profile(char_samp->Width(), 0); for (int x = 0; x < char_samp->Width(); x++) { int min_y = word_hgt; int max_y = -1; line_data = raw_data; for (int y = 0; y < char_samp->Height(); y++, line_data += stride) { if (line_data[x] == 0) { UpdateRange(y + char_samp->Top(), &min_y, &max_y); } } top_profile[x] = 1.0 * (min_y == word_hgt ? 0 : (min_y + 1)) / word_hgt; bottom_profile[x] = 1.0 * (max_y == -1 ? 0 : (word_hgt - max_y)) / word_hgt; } // compute the chebyshev coefficients of each profile ChebyshevCoefficients(left_profile, kChebychevCoefficientCnt, features); ChebyshevCoefficients(top_profile, kChebychevCoefficientCnt, features + kChebychevCoefficientCnt); ChebyshevCoefficients(right_profile, kChebychevCoefficientCnt, features + (2 * kChebychevCoefficientCnt)); ChebyshevCoefficients(bottom_profile, kChebychevCoefficientCnt, features + (3 * kChebychevCoefficientCnt)); return true; } } // namespace tesseract tesseract-3.04.01/cube/feature_chebyshev.h000066400000000000000000000043071266071204500204450ustar00rootroot00000000000000/********************************************************************** * File: feature_chebyshev.h * Description: Declaration of the Chebyshev coefficients Feature Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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. * **********************************************************************/ // The FeatureChebyshev class implements a Bitmap feature extractor class. It // inherits from the FeatureBase class // The feature vector is the composed of the chebyshev coefficients of 4 time // sequences. The time sequences are the left, top, right & bottom // bitmap profiles of the input samples #ifndef FEATURE_CHEBYSHEV_H #define FEATURE_CHEBYSHEV_H #include "char_samp.h" #include "feature_base.h" namespace tesseract { class FeatureChebyshev : public FeatureBase { public: explicit FeatureChebyshev(TuningParams *params); virtual ~FeatureChebyshev(); // Render a visualization of the features to a CharSamp. // This is mainly used by visual-debuggers virtual CharSamp *ComputeFeatureBitmap(CharSamp *samp); // Compute the features for a given CharSamp virtual bool ComputeFeatures(CharSamp *samp, float *features); // Returns the count of features virtual int FeatureCnt() { return (4 * kChebychevCoefficientCnt); } protected: static const int kChebychevCoefficientCnt = 40; // Compute Chebychev coefficients for the specified vector void ChebyshevCoefficients(const vector &input, int coeff_cnt, float *coeff); // Compute the features for a given CharSamp bool ComputeChebyshevCoefficients(CharSamp *samp, float *features); }; } #endif // FEATURE_CHEBYSHEV_H tesseract-3.04.01/cube/feature_hybrid.cpp000066400000000000000000000037611266071204500203040ustar00rootroot00000000000000/********************************************************************** * File: feature_chebyshev.cpp * Description: Implementation of the Chebyshev coefficients Feature Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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 #include #include #include #include #include "feature_base.h" #include "feature_hybrid.h" #include "cube_utils.h" #include "const.h" #include "char_samp.h" namespace tesseract { FeatureHybrid::FeatureHybrid(TuningParams *params) :FeatureBase(params) { feature_bmp_ = new FeatureBmp(params); feature_chebyshev_ = new FeatureChebyshev(params); } FeatureHybrid::~FeatureHybrid() { delete feature_bmp_; delete feature_chebyshev_; } // Render a visualization of the features to a CharSamp. // This is mainly used by visual-debuggers CharSamp *FeatureHybrid::ComputeFeatureBitmap(CharSamp *char_samp) { return char_samp; } // Compute the features of a given CharSamp bool FeatureHybrid::ComputeFeatures(CharSamp *char_samp, float *features) { if (feature_bmp_ == NULL || feature_chebyshev_ == NULL) { return false; } if (!feature_bmp_->ComputeFeatures(char_samp, features)) { return false; } return feature_chebyshev_->ComputeFeatures(char_samp, features + feature_bmp_->FeatureCnt()); } } // namespace tesseract tesseract-3.04.01/cube/feature_hybrid.h000066400000000000000000000037601266071204500177500ustar00rootroot00000000000000/********************************************************************** * File: feature_chebyshev.h * Description: Declaration of the Chebyshev coefficients Feature Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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. * **********************************************************************/ // The FeatureHybrid class implements a Bitmap feature extractor class. It // inherits from the FeatureBase class // This class describes the a hybrid feature vector composed by combining // the bitmap and the chebyshev feature vectors #ifndef FEATURE_HYBRID_H #define FEATURE_HYBRID_H #include "char_samp.h" #include "feature_bmp.h" #include "feature_chebyshev.h" namespace tesseract { class FeatureHybrid : public FeatureBase { public: explicit FeatureHybrid(TuningParams *params); virtual ~FeatureHybrid(); // Render a visualization of the features to a CharSamp. // This is mainly used by visual-debuggers virtual CharSamp *ComputeFeatureBitmap(CharSamp *samp); // Compute the features for a given CharSamp virtual bool ComputeFeatures(CharSamp *samp, float *features); // Returns the count of features virtual int FeatureCnt() { if (feature_bmp_ == NULL || feature_chebyshev_ == NULL) { return 0; } return feature_bmp_->FeatureCnt() + feature_chebyshev_->FeatureCnt(); } protected: FeatureBmp *feature_bmp_; FeatureChebyshev *feature_chebyshev_; }; } #endif // FEATURE_HYBRID_H tesseract-3.04.01/cube/hybrid_neural_net_classifier.cpp000066400000000000000000000264611266071204500232130ustar00rootroot00000000000000/********************************************************************** * File: charclassifier.cpp * Description: Implementation of Convolutional-NeuralNet Character Classifier * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 #include #include #include #include #include "classifier_base.h" #include "char_set.h" #include "const.h" #include "conv_net_classifier.h" #include "cube_utils.h" #include "feature_base.h" #include "feature_bmp.h" #include "hybrid_neural_net_classifier.h" #include "tess_lang_model.h" namespace tesseract { HybridNeuralNetCharClassifier::HybridNeuralNetCharClassifier( CharSet *char_set, TuningParams *params, FeatureBase *feat_extract) : CharClassifier(char_set, params, feat_extract) { net_input_ = NULL; net_output_ = NULL; } HybridNeuralNetCharClassifier::~HybridNeuralNetCharClassifier() { for (int net_idx = 0; net_idx < nets_.size(); net_idx++) { if (nets_[net_idx] != NULL) { delete nets_[net_idx]; } } nets_.clear(); if (net_input_ != NULL) { delete []net_input_; net_input_ = NULL; } if (net_output_ != NULL) { delete []net_output_; net_output_ = NULL; } } // The main training function. Given a sample and a class ID the classifier // updates its parameters according to its learning algorithm. This function // is currently not implemented. TODO(ahmadab): implement end-2-end training bool HybridNeuralNetCharClassifier::Train(CharSamp *char_samp, int ClassID) { return false; } // A secondary function needed for training. Allows the trainer to set the // value of any train-time parameter. This function is currently not // implemented. TODO(ahmadab): implement end-2-end training bool HybridNeuralNetCharClassifier::SetLearnParam(char *var_name, float val) { // TODO(ahmadab): implementation of parameter initializing. return false; } // Folds the output of the NeuralNet using the loaded folding sets void HybridNeuralNetCharClassifier::Fold() { // in case insensitive mode if (case_sensitive_ == false) { int class_cnt = char_set_->ClassCount(); // fold case for (int class_id = 0; class_id < class_cnt; class_id++) { // get class string const char_32 *str32 = char_set_->ClassString(class_id); // get the upper case form of the string string_32 upper_form32 = str32; for (int ch = 0; ch < upper_form32.length(); ch++) { if (iswalpha(static_cast(upper_form32[ch])) != 0) { upper_form32[ch] = towupper(upper_form32[ch]); } } // find out the upperform class-id if any int upper_class_id = char_set_->ClassID(reinterpret_cast( upper_form32.c_str())); if (upper_class_id != -1 && class_id != upper_class_id) { float max_out = MAX(net_output_[class_id], net_output_[upper_class_id]); net_output_[class_id] = max_out; net_output_[upper_class_id] = max_out; } } } // The folding sets specify how groups of classes should be folded // Folding involved assigning a min-activation to all the members // of the folding set. The min-activation is a fraction of the max-activation // of the members of the folding set for (int fold_set = 0; fold_set < fold_set_cnt_; fold_set++) { float max_prob = net_output_[fold_sets_[fold_set][0]]; for (int ch = 1; ch < fold_set_len_[fold_set]; ch++) { if (net_output_[fold_sets_[fold_set][ch]] > max_prob) { max_prob = net_output_[fold_sets_[fold_set][ch]]; } } for (int ch = 0; ch < fold_set_len_[fold_set]; ch++) { net_output_[fold_sets_[fold_set][ch]] = MAX(max_prob * kFoldingRatio, net_output_[fold_sets_[fold_set][ch]]); } } } // compute the features of specified charsamp and // feedforward the specified nets bool HybridNeuralNetCharClassifier::RunNets(CharSamp *char_samp) { int feat_cnt = feat_extract_->FeatureCnt(); int class_cnt = char_set_->ClassCount(); // allocate i/p and o/p buffers if needed if (net_input_ == NULL) { net_input_ = new float[feat_cnt]; if (net_input_ == NULL) { return false; } net_output_ = new float[class_cnt]; if (net_output_ == NULL) { return false; } } // compute input features if (feat_extract_->ComputeFeatures(char_samp, net_input_) == false) { return false; } // go through all the nets memset(net_output_, 0, class_cnt * sizeof(*net_output_)); float *inputs = net_input_; for (int net_idx = 0; net_idx < nets_.size(); net_idx++) { // run each net vector net_out(class_cnt, 0.0); if (!nets_[net_idx]->FeedForward(inputs, &net_out[0])) { return false; } // add the output values for (int class_idx = 0; class_idx < class_cnt; class_idx++) { net_output_[class_idx] += (net_out[class_idx] * net_wgts_[net_idx]); } // increment inputs pointer inputs += nets_[net_idx]->in_cnt(); } Fold(); return true; } // return the cost of being a char int HybridNeuralNetCharClassifier::CharCost(CharSamp *char_samp) { // it is by design that a character cost is equal to zero // when no nets are present. This is the case during training. if (RunNets(char_samp) == false) { return 0; } return CubeUtils::Prob2Cost(1.0f - net_output_[0]); } // classifies a charsamp and returns an alternate list // of chars sorted by char costs CharAltList *HybridNeuralNetCharClassifier::Classify(CharSamp *char_samp) { // run the needed nets if (RunNets(char_samp) == false) { return NULL; } int class_cnt = char_set_->ClassCount(); // create an altlist CharAltList *alt_list = new CharAltList(char_set_, class_cnt); if (alt_list == NULL) { return NULL; } for (int out = 1; out < class_cnt; out++) { int cost = CubeUtils::Prob2Cost(net_output_[out]); alt_list->Insert(out, cost); } return alt_list; } // set an external net (for training purposes) void HybridNeuralNetCharClassifier::SetNet(tesseract::NeuralNet *char_net) { } // Load folding sets // This function returns true on success or if the file can't be read, // returns false if an error is encountered. bool HybridNeuralNetCharClassifier::LoadFoldingSets( const string &data_file_path, const string &lang, LangModel *lang_mod) { fold_set_cnt_ = 0; string fold_file_name; fold_file_name = data_file_path + lang; fold_file_name += ".cube.fold"; // folding sets are optional FILE *fp = fopen(fold_file_name.c_str(), "rb"); if (fp == NULL) { return true; } fclose(fp); string fold_sets_str; if (!CubeUtils::ReadFileToString(fold_file_name, &fold_sets_str)) { return false; } // split into lines vector str_vec; CubeUtils::SplitStringUsing(fold_sets_str, "\r\n", &str_vec); fold_set_cnt_ = str_vec.size(); fold_sets_ = new int *[fold_set_cnt_]; if (fold_sets_ == NULL) { return false; } fold_set_len_ = new int[fold_set_cnt_]; if (fold_set_len_ == NULL) { fold_set_cnt_ = 0; return false; } for (int fold_set = 0; fold_set < fold_set_cnt_; fold_set++) { reinterpret_cast(lang_mod)->RemoveInvalidCharacters( &str_vec[fold_set]); // if all or all but one character are invalid, invalidate this set if (str_vec[fold_set].length() <= 1) { fprintf(stderr, "Cube WARNING (ConvNetCharClassifier::LoadFoldingSets): " "invalidating folding set %d\n", fold_set); fold_set_len_[fold_set] = 0; fold_sets_[fold_set] = NULL; continue; } string_32 str32; CubeUtils::UTF8ToUTF32(str_vec[fold_set].c_str(), &str32); fold_set_len_[fold_set] = str32.length(); fold_sets_[fold_set] = new int[fold_set_len_[fold_set]]; if (fold_sets_[fold_set] == NULL) { fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::LoadFoldingSets): " "could not allocate folding set\n"); fold_set_cnt_ = fold_set; return false; } for (int ch = 0; ch < fold_set_len_[fold_set]; ch++) { fold_sets_[fold_set][ch] = char_set_->ClassID(str32[ch]); } } return true; } // Init the classifier provided a data-path and a language string bool HybridNeuralNetCharClassifier::Init(const string &data_file_path, const string &lang, LangModel *lang_mod) { if (init_ == true) { return true; } // load the nets if any. This function will return true if the net file // does not exist. But will fail if the net did not pass the sanity checks if (!LoadNets(data_file_path, lang)) { return false; } // load the folding sets if any. This function will return true if the // file does not exist. But will fail if the it did not pass the sanity checks if (!LoadFoldingSets(data_file_path, lang, lang_mod)) { return false; } init_ = true; return true; } // Load the classifier's Neural Nets // This function will return true if the net file does not exist. // But will fail if the net did not pass the sanity checks bool HybridNeuralNetCharClassifier::LoadNets(const string &data_file_path, const string &lang) { string hybrid_net_file; string junk_net_file; // add the lang identifier hybrid_net_file = data_file_path + lang; hybrid_net_file += ".cube.hybrid"; // neural network is optional FILE *fp = fopen(hybrid_net_file.c_str(), "rb"); if (fp == NULL) { return true; } fclose(fp); string str; if (!CubeUtils::ReadFileToString(hybrid_net_file, &str)) { return false; } // split into lines vector str_vec; CubeUtils::SplitStringUsing(str, "\r\n", &str_vec); if (str_vec.size() <= 0) { return false; } // create and add the nets nets_.resize(str_vec.size(), NULL); net_wgts_.resize(str_vec.size(), 0); int total_input_size = 0; for (int net_idx = 0; net_idx < str_vec.size(); net_idx++) { // parse the string vector tokens_vec; CubeUtils::SplitStringUsing(str_vec[net_idx], " \t", &tokens_vec); // has to be 2 tokens, net name and input size if (tokens_vec.size() != 2) { return false; } // load the net string net_file_name = data_file_path + tokens_vec[0]; nets_[net_idx] = tesseract::NeuralNet::FromFile(net_file_name); if (nets_[net_idx] == NULL) { return false; } // parse the input size and validate it net_wgts_[net_idx] = atof(tokens_vec[1].c_str()); if (net_wgts_[net_idx] < 0.0) { return false; } total_input_size += nets_[net_idx]->in_cnt(); } // validate total input count if (total_input_size != feat_extract_->FeatureCnt()) { return false; } // success return true; } } // tesseract tesseract-3.04.01/cube/hybrid_neural_net_classifier.h000066400000000000000000000073651266071204500226620ustar00rootroot00000000000000/********************************************************************** * File: conv_net_classifier.h * Description: Declaration of Convolutional-NeuralNet Character Classifier * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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 HYBRID_NEURAL_NET_CLASSIFIER_H #define HYBRID_NEURAL_NET_CLASSIFIER_H #include #include #include "char_samp.h" #include "char_altlist.h" #include "char_set.h" #include "classifier_base.h" #include "feature_base.h" #include "lang_model.h" #include "neural_net.h" #include "tuning_params.h" namespace tesseract { // Folding Ratio is the ratio of the max-activation of members of a folding // set that is used to compute the min-activation of the rest of the set // static const float kFoldingRatio = 0.75; // see conv_net_classifier.h class HybridNeuralNetCharClassifier : public CharClassifier { public: HybridNeuralNetCharClassifier(CharSet *char_set, TuningParams *params, FeatureBase *feat_extract); virtual ~HybridNeuralNetCharClassifier(); // The main training function. Given a sample and a class ID the classifier // updates its parameters according to its learning algorithm. This function // is currently not implemented. TODO(ahmadab): implement end-2-end training virtual bool Train(CharSamp *char_samp, int ClassID); // A secondary function needed for training. Allows the trainer to set the // value of any train-time parameter. This function is currently not // implemented. TODO(ahmadab): implement end-2-end training virtual bool SetLearnParam(char *var_name, float val); // Externally sets the Neural Net used by the classifier. Used for training void SetNet(tesseract::NeuralNet *net); // Classifies an input charsamp and return a CharAltList object containing // the possible candidates and corresponding scores virtual CharAltList *Classify(CharSamp *char_samp); // Computes the cost of a specific charsamp being a character (versus a // non-character: part-of-a-character OR more-than-one-character) virtual int CharCost(CharSamp *char_samp); private: // Neural Net object used for classification vector nets_; vector net_wgts_; // data buffers used to hold Neural Net inputs and outputs float *net_input_; float *net_output_; // Init the classifier provided a data-path and a language string virtual bool Init(const string &data_file_path, const string &lang, LangModel *lang_mod); // Loads the NeuralNets needed for the classifier bool LoadNets(const string &data_file_path, const string &lang); // Load folding sets // This function returns true on success or if the file can't be read, // returns false if an error is encountered. virtual bool LoadFoldingSets(const string &data_file_path, const string &lang, LangModel *lang_mod); // Folds the output of the NeuralNet using the loaded folding sets virtual void Fold(); // Scales the input char_samp and feeds it to the NeuralNet as input bool RunNets(CharSamp *char_samp); }; } #endif // HYBRID_NEURAL_NET_CLASSIFIER_H tesseract-3.04.01/cube/lang_mod_edge.h000066400000000000000000000054271266071204500175220ustar00rootroot00000000000000/********************************************************************** * File: lang_mod_edge.h * Description: Declaration of the Language Model Edge Base Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The LangModEdge abstracts an Edge in the language model trie // This is an abstract class that any Language Model Edge should inherit from // It provides methods for: // 1- Returns the class ID corresponding to the edge // 2- If the edge is a valid EndOfWord (EOW) // 3- If the edge is coming from a OutOfDictionary (OOF) state machine // 4- If the edge is a Terminal (has no children) // 5- A Hash of the edge that will be used to retrieve the edge // quickly from the BeamSearch lattice // 6- If two edges are identcial // 7- Returns a verbal description of the edge (use by debuggers) // 8- the language model cost of the edge (if any) // 9- The string corresponding to this edge // 10- Getting and setting the "Root" status of the edge #ifndef LANG_MOD_EDGE_H #define LANG_MOD_EDGE_H #include "cube_tuning_params.h" #include "char_set.h" namespace tesseract { class LangModEdge { public: LangModEdge() {} virtual ~LangModEdge() {} // The string corresponding to this edge virtual const char_32 * EdgeString() const = 0; // Returns the class ID corresponding to the edge virtual int ClassID() const = 0; // If the edge is the root edge virtual bool IsRoot() const = 0; // Set the Root flag virtual void SetRoot(bool flag) = 0; // If the edge is a valid EndOfWord (EOW) virtual bool IsEOW() const = 0; // is the edge is coming from a OutOfDictionary (OOF) state machine virtual bool IsOOD() const = 0; // Is the edge is a Terminal (has no children) virtual bool IsTerminal() const = 0; // Returns A hash of the edge that will be used to retrieve the edge virtual unsigned int Hash() const = 0; // Are the two edges identcial? virtual bool IsIdentical(LangModEdge *edge) const = 0; // a verbal description of the edge (use by debuggers) virtual char *Description() const = 0; // the language model cost of the edge (if any) virtual int PathCost() const = 0; }; } #endif // LANG_MOD_EDGE_H tesseract-3.04.01/cube/lang_model.h000066400000000000000000000056741266071204500170630ustar00rootroot00000000000000/********************************************************************** * File: lang_model.h * Description: Declaration of the Language Model Edge Base Class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // The LanguageModel class abstracts a State machine that is modeled as a Trie // structure. The state machine models the language being recognized by the OCR // Engine // This is an abstract class that is to be inherited by any language model #ifndef LANG_MODEL_H #define LANG_MODEL_H #include "lang_mod_edge.h" #include "char_altlist.h" #include "char_set.h" #include "tuning_params.h" namespace tesseract { class LangModel { public: LangModel() { ood_enabled_ = true; numeric_enabled_ = true; word_list_enabled_ = true; punc_enabled_ = true; } virtual ~LangModel() {} // Returns an edge pointer to the Root virtual LangModEdge *Root() = 0; // Returns the edges that fan-out of the specified edge and their count virtual LangModEdge **GetEdges(CharAltList *alt_list, LangModEdge *parent_edge, int *edge_cnt) = 0; // Returns is a sequence of 32-bit characters are valid within this language // model or net. And EndOfWord flag is specified. If true, the sequence has // to end on a valid word. The function also optionally returns the list // of language model edges traversed to parse the string virtual bool IsValidSequence(const char_32 *str, bool eow_flag, LangModEdge **edge_array = NULL) = 0; virtual bool IsLeadingPunc(char_32 ch) = 0; virtual bool IsTrailingPunc(char_32 ch) = 0; virtual bool IsDigit(char_32 ch) = 0; // accessor functions inline bool OOD() { return ood_enabled_; } inline bool Numeric() { return numeric_enabled_; } inline bool WordList() { return word_list_enabled_; } inline bool Punc() { return punc_enabled_; } inline void SetOOD(bool ood) { ood_enabled_ = ood; } inline void SetNumeric(bool numeric) { numeric_enabled_ = numeric; } inline void SetWordList(bool word_list) { word_list_enabled_ = word_list; } inline void SetPunc(bool punc_enabled) { punc_enabled_ = punc_enabled; } protected: bool ood_enabled_; bool numeric_enabled_; bool word_list_enabled_; bool punc_enabled_; }; } #endif // LANG_MODEL_H tesseract-3.04.01/cube/search_column.cpp000066400000000000000000000142761266071204500201350ustar00rootroot00000000000000/********************************************************************** * File: search_column.cpp * Description: Implementation of the Beam Search Column Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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 "search_column.h" #include namespace tesseract { SearchColumn::SearchColumn(int col_idx, int max_node) { col_idx_ = col_idx; node_cnt_ = 0; node_array_ = NULL; max_node_cnt_ = max_node; node_hash_table_ = NULL; init_ = false; min_cost_ = INT_MAX; max_cost_ = 0; } // Cleanup data void SearchColumn::Cleanup() { if (node_array_ != NULL) { for (int node_idx = 0; node_idx < node_cnt_; node_idx++) { if (node_array_[node_idx] != NULL) { delete node_array_[node_idx]; } } delete []node_array_; node_array_ = NULL; } FreeHashTable(); init_ = false; } SearchColumn::~SearchColumn() { Cleanup(); } // Initializations bool SearchColumn::Init() { if (init_ == true) { return true; } // create hash table if (node_hash_table_ == NULL) { node_hash_table_ = new SearchNodeHashTable(); if (node_hash_table_ == NULL) { return false; } } init_ = true; return true; } // Prune the nodes if necessary. Pruning is done such that a max // number of nodes is kept, i.e., the beam width void SearchColumn::Prune() { // no need to prune if (node_cnt_ <= max_node_cnt_) { return; } // compute the cost histogram memset(score_bins_, 0, sizeof(score_bins_)); int cost_range = max_cost_ - min_cost_ + 1; for (int node_idx = 0; node_idx < node_cnt_; node_idx++) { int cost_bin = static_cast( ((node_array_[node_idx]->BestCost() - min_cost_) * kScoreBins) / static_cast(cost_range)); if (cost_bin >= kScoreBins) { cost_bin = kScoreBins - 1; } score_bins_[cost_bin]++; } // determine the pruning cost by scanning the cost histogram from // least to greatest cost bins and finding the cost at which the // max number of nodes is exceeded int pruning_cost = 0; int new_node_cnt = 0; for (int cost_bin = 0; cost_bin < kScoreBins; cost_bin++) { if (new_node_cnt > 0 && (new_node_cnt + score_bins_[cost_bin]) > max_node_cnt_) { pruning_cost = min_cost_ + ((cost_bin * cost_range) / kScoreBins); break; } new_node_cnt += score_bins_[cost_bin]; } // prune out all the nodes above this cost for (int node_idx = new_node_cnt = 0; node_idx < node_cnt_; node_idx++) { // prune this node out if (node_array_[node_idx]->BestCost() > pruning_cost || new_node_cnt > max_node_cnt_) { delete node_array_[node_idx]; } else { // keep it node_array_[new_node_cnt++] = node_array_[node_idx]; } } node_cnt_ = new_node_cnt; } // sort all nodes void SearchColumn::Sort() { if (node_cnt_ > 0 && node_array_ != NULL) { qsort(node_array_, node_cnt_, sizeof(*node_array_), SearchNode::SearchNodeComparer); } } // add a new node SearchNode *SearchColumn::AddNode(LangModEdge *edge, int reco_cost, SearchNode *parent_node, CubeRecoContext *cntxt) { // init if necessary if (init_ == false && Init() == false) { return NULL; } // find out if we have an node with the same edge // look in the hash table SearchNode *new_node = node_hash_table_->Lookup(edge, parent_node); // node does not exist if (new_node == NULL) { new_node = new SearchNode(cntxt, parent_node, reco_cost, edge, col_idx_); if (new_node == NULL) { return NULL; } // if the max node count has already been reached, check if the cost of // the new node exceeds the max cost. This indicates that it will be pruned // and so there is no point adding it if (node_cnt_ >= max_node_cnt_ && new_node->BestCost() > max_cost_) { delete new_node; return NULL; } // expand the node buffer if necc if ((node_cnt_ % kNodeAllocChunk) == 0) { // alloc a new buff SearchNode **new_node_buff = new SearchNode *[node_cnt_ + kNodeAllocChunk]; if (new_node_buff == NULL) { delete new_node; return NULL; } // free existing after copying contents if (node_array_ != NULL) { memcpy(new_node_buff, node_array_, node_cnt_ * sizeof(*new_node_buff)); delete []node_array_; } node_array_ = new_node_buff; } // add the node to the hash table only if it is non-OOD edge // because the langmod state is not unique if (edge->IsOOD() == false) { if (!node_hash_table_->Insert(edge, new_node)) { tprintf("Hash table full!!!"); delete new_node; return NULL; } } node_array_[node_cnt_++] = new_node; } else { // node exists before // if no update occurred, return NULL if (new_node->UpdateParent(parent_node, reco_cost, edge) == false) { new_node = NULL; } // free the edge if (edge != NULL) { delete edge; } } // update Min and Max Costs if (new_node != NULL) { if (min_cost_ > new_node->BestCost()) { min_cost_ = new_node->BestCost(); } if (max_cost_ < new_node->BestCost()) { max_cost_ = new_node->BestCost(); } } return new_node; } SearchNode *SearchColumn::BestNode() { SearchNode *best_node = NULL; for (int node_idx = 0; node_idx < node_cnt_; node_idx++) { if (best_node == NULL || best_node->BestCost() > node_array_[node_idx]->BestCost()) { best_node = node_array_[node_idx]; } } return best_node; } } // namespace tesseract tesseract-3.04.01/cube/search_column.h000066400000000000000000000053531266071204500175760ustar00rootroot00000000000000/********************************************************************** * File: search_column.h * Description: Declaration of the Beam Search Column Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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. * **********************************************************************/ // The SearchColumn class abstracts a column in the lattice that is created // by the BeamSearch during the recognition process // The class holds the lattice nodes. New nodes are added by calls to AddNode // made from the BeamSearch // The class maintains a hash table of the nodes to be able to lookup nodes // quickly using their lang_mod_edge. This is needed to merge similar paths // in the lattice #ifndef SEARCH_COLUMN_H #define SEARCH_COLUMN_H #include "search_node.h" #include "lang_mod_edge.h" #include "cube_reco_context.h" namespace tesseract { class SearchColumn { public: SearchColumn(int col_idx, int max_node_cnt); ~SearchColumn(); // Accessor functions inline int ColIdx() const { return col_idx_; } inline int NodeCount() const { return node_cnt_; } inline SearchNode **Nodes() const { return node_array_; } // Prune the nodes if necessary. Pruning is done such that a max // number of nodes is kept, i.e., the beam width void Prune(); SearchNode *AddNode(LangModEdge *edge, int score, SearchNode *parent, CubeRecoContext *cntxt); // Returns the node with the least cost SearchNode *BestNode(); // Sort the lattice nodes. Needed for visualization void Sort(); // Free up the Hash Table. Added to be called by the Beam Search after // a column is pruned to reduce memory foot print void FreeHashTable() { if (node_hash_table_ != NULL) { delete node_hash_table_; node_hash_table_ = NULL; } } private: static const int kNodeAllocChunk = 1024; static const int kScoreBins = 1024; bool init_; int min_cost_; int max_cost_; int max_node_cnt_; int node_cnt_; int col_idx_; int score_bins_[kScoreBins]; SearchNode **node_array_; SearchNodeHashTable *node_hash_table_; // Free node array and hash table void Cleanup(); // Create hash table bool Init(); }; } #endif // SEARCH_COLUMN_H tesseract-3.04.01/cube/search_node.cpp000066400000000000000000000165761266071204500175720ustar00rootroot00000000000000/********************************************************************** * File: search_node.cpp * Description: Implementation of the Beam Search Node Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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 "search_node.h" namespace tesseract { // The constructor updates the best paths and costs: // mean_char_reco_cost_ (returned by BestRecoCost()) is the mean // char_reco cost of the best_path, including this node. // best_path_reco_cost is the total char_reco_cost of the best_path, // but excludes the char_reco_cost of this node. // best_cost is the mean mixed cost, i.e., mean_char_reco_cost_ + // current language model cost, all weighted by the cube context's // RecoWgt parameter SearchNode::SearchNode(CubeRecoContext *cntxt, SearchNode *parent_node, int char_reco_cost, LangModEdge *edge, int col_idx) { // copy data members cntxt_ = cntxt; lang_mod_edge_ = edge; col_idx_ = col_idx; parent_node_ = parent_node; char_reco_cost_ = char_reco_cost; // the string of this node is the same as that of the language model edge str_ = (edge == NULL ? NULL : edge->EdgeString()); // compute best path total reco cost best_path_reco_cost_ = (parent_node_ == NULL) ? 0 : parent_node_->CharRecoCost() + parent_node_->BestPathRecoCost(); // update best path length best_path_len_ = (parent_node_ == NULL) ? 1 : parent_node_->BestPathLength() + 1; if (edge != NULL && edge->IsRoot() && parent_node_ != NULL) { best_path_len_++; } // compute best reco cost mean cost mean_char_reco_cost_ = static_cast( (best_path_reco_cost_ + char_reco_cost_) / static_cast(best_path_len_)); // get language model cost int lm_cost = LangModCost(lang_mod_edge_, parent_node_); // compute aggregate best cost best_cost_ = static_cast(cntxt_->Params()->RecoWgt() * (best_path_reco_cost_ + char_reco_cost_) / static_cast(best_path_len_) ) + lm_cost; } SearchNode::~SearchNode() { if (lang_mod_edge_ != NULL) { delete lang_mod_edge_; } } // update the parent_node node if provides a better (less) cost bool SearchNode::UpdateParent(SearchNode *new_parent, int new_reco_cost, LangModEdge *new_edge) { if (lang_mod_edge_ == NULL) { if (new_edge != NULL) { return false; } } else { // to update the parent_node, we have to have the same target // state and char if (new_edge == NULL || !lang_mod_edge_->IsIdentical(new_edge) || !SearchNode::IdenticalPath(parent_node_, new_parent)) { return false; } } // compute the path cost and combined cost of the new path int new_best_path_reco_cost; int new_cost; int new_best_path_len; new_best_path_reco_cost = (new_parent == NULL) ? 0 : new_parent->BestPathRecoCost() + new_parent->CharRecoCost(); new_best_path_len = (new_parent == NULL) ? 1 : new_parent->BestPathLength() + 1; // compute the new language model cost int new_lm_cost = LangModCost(new_edge, new_parent); new_cost = static_cast(cntxt_->Params()->RecoWgt() * (new_best_path_reco_cost + new_reco_cost) / static_cast(new_best_path_len) ) + new_lm_cost; // update if it is better (less) than the current one if (best_cost_ > new_cost) { parent_node_ = new_parent; char_reco_cost_ = new_reco_cost; best_path_reco_cost_ = new_best_path_reco_cost; best_path_len_ = new_best_path_len; mean_char_reco_cost_ = static_cast( (best_path_reco_cost_ + char_reco_cost_) / static_cast(best_path_len_)); best_cost_ = static_cast(cntxt_->Params()->RecoWgt() * (best_path_reco_cost_ + char_reco_cost_) / static_cast(best_path_len_) ) + new_lm_cost; return true; } return false; } char_32 *SearchNode::PathString() { SearchNode *node = this; // compute string length int len = 0; while (node != NULL) { if (node->str_ != NULL) { len += CubeUtils::StrLen(node->str_); } // if the edge is a root and does not have a NULL parent, account for space LangModEdge *lm_edge = node->LangModelEdge(); if (lm_edge != NULL && lm_edge->IsRoot() && node->ParentNode() != NULL) { len++; } node = node->parent_node_; } char_32 *char_ptr = new char_32[len + 1]; if (char_ptr == NULL) { return NULL; } int ch_idx = len; node = this; char_ptr[ch_idx--] = 0; while (node != NULL) { int str_len = ((node->str_ == NULL) ? 0 : CubeUtils::StrLen(node->str_)); while (str_len > 0) { char_ptr[ch_idx--] = node->str_[--str_len]; } // if the edge is a root and does not have a NULL parent, insert a space LangModEdge *lm_edge = node->LangModelEdge(); if (lm_edge != NULL && lm_edge->IsRoot() && node->ParentNode() != NULL) { char_ptr[ch_idx--] = (char_32)' '; } node = node->parent_node_; } return char_ptr; } // compares the path of two nodes and checks if its identical bool SearchNode::IdenticalPath(SearchNode *node1, SearchNode *node2) { if (node1 != NULL && node2 != NULL && node1->best_path_len_ != node2->best_path_len_) { return false; } // backtrack until either a root or a NULL edge is reached while (node1 != NULL && node2 != NULL) { if (node1->str_ != node2->str_) { return false; } // stop if either nodes is a root if (node1->LangModelEdge()->IsRoot() || node2->LangModelEdge()->IsRoot()) { break; } node1 = node1->parent_node_; node2 = node2->parent_node_; } return ((node1 == NULL && node2 == NULL) || (node1 != NULL && node1->LangModelEdge()->IsRoot() && node2 != NULL && node2->LangModelEdge()->IsRoot())); } // Computes the language model cost of a path int SearchNode::LangModCost(LangModEdge *current_lm_edge, SearchNode *parent_node) { int lm_cost = 0; int node_cnt = 0; do { // check if root bool is_root = ((current_lm_edge != NULL && current_lm_edge->IsRoot()) || parent_node == NULL); if (is_root) { node_cnt++; lm_cost += (current_lm_edge == NULL ? 0 : current_lm_edge->PathCost()); } // continue until we hit a null parent if (parent_node == NULL) { break; } // get the previous language model edge current_lm_edge = parent_node->LangModelEdge(); // back track parent_node = parent_node->ParentNode(); } while (true); return static_cast(lm_cost / static_cast(node_cnt)); } } // namespace tesseract tesseract-3.04.01/cube/search_node.h000066400000000000000000000144661266071204500172330ustar00rootroot00000000000000/********************************************************************** * File: search_node.h * Description: Declaration of the Beam Search Node Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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. * **********************************************************************/ // The SearchNode class abstracts the search lattice node in the lattice // generated by the BeamSearch class // The SearchNode class holds the lang_mod_edge associated with the lattice // node. It also holds a pointer to the parent SearchNode in the search path // In addition it holds the recognition and the language model costs of the // node and the path leading to this node #ifndef SEARCH_NODE_H #define SEARCH_NODE_H #include "lang_mod_edge.h" #include "cube_reco_context.h" namespace tesseract { class SearchNode { public: SearchNode(CubeRecoContext *cntxt, SearchNode *parent_node, int char_reco_cost, LangModEdge *edge, int col_idx); ~SearchNode(); // Updates the parent of the current node if the specified path yields // a better path cost bool UpdateParent(SearchNode *new_parent, int new_reco_cost, LangModEdge *new_edge); // returns the 32-bit string corresponding to the path leading to this node char_32 *PathString(); // True if the two input nodes correspond to the same path static bool IdenticalPath(SearchNode *node1, SearchNode *node2); inline const char_32 *NodeString() { return str_; } inline void SetString(char_32 *str) { str_ = str; } // This node's character recognition cost. inline int CharRecoCost() { return char_reco_cost_; } // Total character recognition cost of the nodes in the best path, // excluding this node. inline int BestPathRecoCost() { return best_path_reco_cost_; } // Number of nodes in best path. inline int BestPathLength() { return best_path_len_; } // Mean mixed cost, i.e., mean character recognition cost + // current language model cost, all weighted by the RecoWgt parameter inline int BestCost() { return best_cost_; } // Mean character recognition cost of the nodes on the best path, // including this node. inline int BestRecoCost() { return mean_char_reco_cost_ ; } inline int ColIdx() { return col_idx_; } inline SearchNode *ParentNode() { return parent_node_; } inline LangModEdge *LangModelEdge() { return lang_mod_edge_;} inline int LangModCost() { return LangModCost(lang_mod_edge_, parent_node_); } // A comparer function that allows the SearchColumn class to sort the // nodes based on the path cost inline static int SearchNodeComparer(const void *node1, const void *node2) { return (*(reinterpret_cast(node1)))->best_cost_ - (*(reinterpret_cast(node2)))->best_cost_; } private: CubeRecoContext *cntxt_; // Character code const char_32 *str_; // Recognition cost of most recent character int char_reco_cost_; // Mean mixed cost, i.e., mean character recognition cost + // current language model cost, all weighted by the RecoWgt parameter int best_cost_; // Mean character recognition cost of the nodes on the best path, // including this node. int mean_char_reco_cost_ ; // Total character recognition cost of the nodes in the best path, // excluding this node. int best_path_reco_cost_; // Number of nodes in best path. int best_path_len_; // Column index int col_idx_; // Parent Node SearchNode *parent_node_; // Language model edge LangModEdge *lang_mod_edge_; static int LangModCost(LangModEdge *lang_mod_edge, SearchNode *parent_node); }; // Implments a SearchNode hash table used to detect if a Search Node exists // or not. This is needed to make sure that identical paths in the BeamSearch // converge class SearchNodeHashTable { public: SearchNodeHashTable() { memset(bin_size_array_, 0, sizeof(bin_size_array_)); } ~SearchNodeHashTable() { } // inserts an entry in the hash table inline bool Insert(LangModEdge *lang_mod_edge, SearchNode *srch_node) { // compute hash based on the edge and its parent node edge unsigned int edge_hash = lang_mod_edge->Hash(); unsigned int parent_hash = (srch_node->ParentNode() == NULL ? 0 : srch_node->ParentNode()->LangModelEdge()->Hash()); unsigned int hash_bin = (edge_hash + parent_hash) % kSearchNodeHashBins; // already maxed out, just fail if (bin_size_array_[hash_bin] >= kMaxSearchNodePerBin) { return false; } bin_array_[hash_bin][bin_size_array_[hash_bin]++] = srch_node; return true; } // Looks up an entry in the hash table inline SearchNode *Lookup(LangModEdge *lang_mod_edge, SearchNode *parent_node) { // compute hash based on the edge and its parent node edge unsigned int edge_hash = lang_mod_edge->Hash(); unsigned int parent_hash = (parent_node == NULL ? 0 : parent_node->LangModelEdge()->Hash()); unsigned int hash_bin = (edge_hash + parent_hash) % kSearchNodeHashBins; // lookup the entries in the hash bin for (int node_idx = 0; node_idx < bin_size_array_[hash_bin]; node_idx++) { if (lang_mod_edge->IsIdentical( bin_array_[hash_bin][node_idx]->LangModelEdge()) == true && SearchNode::IdenticalPath( bin_array_[hash_bin][node_idx]->ParentNode(), parent_node) == true) { return bin_array_[hash_bin][node_idx]; } } return NULL; } private: // Hash bin size parameters. These were determined emperically. These affect // the speed of the beam search but have no impact on accuracy static const int kSearchNodeHashBins = 4096; static const int kMaxSearchNodePerBin = 512; int bin_size_array_[kSearchNodeHashBins]; SearchNode *bin_array_[kSearchNodeHashBins][kMaxSearchNodePerBin]; }; } #endif // SEARCH_NODE_H tesseract-3.04.01/cube/search_object.h000066400000000000000000000036511266071204500175460ustar00rootroot00000000000000/********************************************************************** * File: search_object.h * Description: Declaration of the Beam Search Object Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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. * **********************************************************************/ // The SearchObject class represents a char_samp (a word bitmap) that is // being searched for characters (or recognizeable entities). // This is an abstract class that all SearchObjects should inherit from // A SearchObject class provides methods to: // 1- Returns the count of segments // 2- Recognize a segment range // 3- Creates a CharSamp for a segment range #ifndef SEARCH_OBJECT_H #define SEARCH_OBJECT_H #include "char_altlist.h" #include "char_samp.h" #include "cube_reco_context.h" namespace tesseract { class SearchObject { public: explicit SearchObject(CubeRecoContext *cntxt) { cntxt_ = cntxt; } virtual ~SearchObject() {} virtual int SegPtCnt() = 0; virtual CharAltList *RecognizeSegment(int start_pt, int end_pt) = 0; virtual CharSamp *CharSample(int start_pt, int end_pt) = 0; virtual Box* CharBox(int start_pt, int end_pt) = 0; virtual int SpaceCost(int seg_pt) = 0; virtual int NoSpaceCost(int seg_pt) = 0; virtual int NoSpaceCost(int start_pt, int end_pt) = 0; protected: CubeRecoContext *cntxt_; }; } #endif // SEARCH_OBJECT_H tesseract-3.04.01/cube/string_32.h000066400000000000000000000024411266071204500165610ustar00rootroot00000000000000/********************************************************************** * File: string_32.h * Description: Declaration of a 32 Bit string class * Author: Ahmad Abdulkader * Created: 2007 * * (C) Copyright 2008, 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. * **********************************************************************/ // the string_32 class provides the functionality needed // for a 32-bit string class #ifndef STRING_32_H #define STRING_32_H #include #include #include #include #ifdef USE_STD_NAMESPACE using std::basic_string; using std::string; using std::vector; #endif namespace tesseract { // basic definitions typedef signed int char_32; typedef basic_string string_32; } #endif // STRING_32_H tesseract-3.04.01/cube/tess_lang_mod_edge.cpp000066400000000000000000000073321266071204500211100ustar00rootroot00000000000000/********************************************************************** * File: tess_lang_mod_edge.cpp * Description: Implementation of the Tesseract Language Model Edge Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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 "tess_lang_mod_edge.h" #include "const.h" #include "unichar.h" namespace tesseract { // OOD constructor TessLangModEdge::TessLangModEdge(CubeRecoContext *cntxt, int class_id) { root_ = false; cntxt_ = cntxt; dawg_ = NULL; start_edge_ = 0; end_edge_ = 0; edge_mask_ = 0; class_id_ = class_id; str_ = cntxt_->CharacterSet()->ClassString(class_id); path_cost_ = Cost(); } /** * leading, trailing punc constructor and single byte UTF char */ TessLangModEdge::TessLangModEdge(CubeRecoContext *cntxt, const Dawg *dawg, EDGE_REF edge_idx, int class_id) { root_ = false; cntxt_ = cntxt; dawg_ = dawg; start_edge_ = edge_idx; end_edge_ = edge_idx; edge_mask_ = 0; class_id_ = class_id; str_ = cntxt_->CharacterSet()->ClassString(class_id); path_cost_ = Cost(); } /** * dict constructor: multi byte UTF char */ TessLangModEdge::TessLangModEdge(CubeRecoContext *cntxt, const Dawg *dawg, EDGE_REF start_edge_idx, EDGE_REF end_edge_idx, int class_id) { root_ = false; cntxt_ = cntxt; dawg_ = dawg; start_edge_ = start_edge_idx; end_edge_ = end_edge_idx; edge_mask_ = 0; class_id_ = class_id; str_ = cntxt_->CharacterSet()->ClassString(class_id); path_cost_ = Cost(); } char *TessLangModEdge::Description() const { char *char_ptr = new char[256]; if (!char_ptr) { return NULL; } char dawg_str[256]; char edge_str[32]; if (dawg_ == (Dawg *)DAWG_OOD) { strcpy(dawg_str, "OOD"); } else if (dawg_ == (Dawg *)DAWG_NUMBER) { strcpy(dawg_str, "NUM"); } else if (dawg_->permuter() == SYSTEM_DAWG_PERM) { strcpy(dawg_str, "Main"); } else if (dawg_->permuter() == USER_DAWG_PERM) { strcpy(dawg_str, "User"); } else if (dawg_->permuter() == DOC_DAWG_PERM) { strcpy(dawg_str, "Doc"); } else { strcpy(dawg_str, "N/A"); } sprintf(edge_str, "%d", static_cast(start_edge_)); if (IsLeadingPuncEdge(edge_mask_)) { strcat(edge_str, "-LP"); } if (IsTrailingPuncEdge(edge_mask_)) { strcat(edge_str, "-TP"); } sprintf(char_ptr, "%s(%s)%s, Wtd Dawg Cost=%d", dawg_str, edge_str, IsEOW() ? "-EOW-" : "", path_cost_); return char_ptr; } int TessLangModEdge::CreateChildren(CubeRecoContext *cntxt, const Dawg *dawg, NODE_REF parent_node, LangModEdge **edge_array) { int edge_cnt = 0; NodeChildVector vec; 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; edge_array[edge_cnt] = new TessLangModEdge(cntxt, dawg, child.edge_ref, child.unichar_id); if (edge_array[edge_cnt] != NULL) edge_cnt++; } return edge_cnt; } } tesseract-3.04.01/cube/tess_lang_mod_edge.h000066400000000000000000000170401266071204500205520ustar00rootroot00000000000000/********************************************************************** * File: tess_lang_mod_edge.h * Description: Declaration of the Tesseract Language Model Edge Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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. * **********************************************************************/ // The TessLangModEdge models an edge in the Tesseract language models // It inherits from the LangModEdge class #ifndef TESS_LANG_MOD_EDGE_H #define TESS_LANG_MOD_EDGE_H #include "dawg.h" #include "char_set.h" #include "lang_mod_edge.h" #include "cube_reco_context.h" #include "cube_utils.h" // Macros needed to identify punctuation in the langmodel state #ifdef _HMSW32_H #define LEAD_PUNC_EDGE_REF_MASK (inT64) 0x0000000100000000i64 #define TRAIL_PUNC_EDGE_REF_MASK (inT64) 0x0000000200000000i64 #define TRAIL_PUNC_REPEAT_MASK (inT64) 0xffff000000000000i64 #else #define LEAD_PUNC_EDGE_REF_MASK (inT64) 0x0000000100000000ll #define TRAIL_PUNC_EDGE_REF_MASK (inT64) 0x0000000200000000ll #define TRAIL_PUNC_REPEAT_MASK (inT64) 0xffff000000000000ll #endif // Number state machine macros #define NUMBER_STATE_SHIFT 0 #define NUMBER_STATE_MASK 0x0000000fl #define NUMBER_LITERAL_SHIFT 4 #define NUMBER_LITERAL_MASK 0x000000f0l #define NUMBER_REPEAT_SHIFT 8 #define NUMBER_REPEAT_MASK 0x00000f00l #define NUM_TRM -99 #define TRAIL_PUNC_REPEAT_SHIFT 48 #define IsLeadingPuncEdge(edge_mask) \ ((edge_mask & LEAD_PUNC_EDGE_REF_MASK) != 0) #define IsTrailingPuncEdge(edge_mask) \ ((edge_mask & TRAIL_PUNC_EDGE_REF_MASK) != 0) #define TrailingPuncCount(edge_mask) \ ((edge_mask & TRAIL_PUNC_REPEAT_MASK) >> TRAIL_PUNC_REPEAT_SHIFT) #define TrailingPuncEdgeMask(Cnt) \ (TRAIL_PUNC_EDGE_REF_MASK | ((Cnt) << TRAIL_PUNC_REPEAT_SHIFT)) // State machine IDs #define DAWG_OOD 0 #define DAWG_NUMBER 1 namespace tesseract { class TessLangModEdge : public LangModEdge { public: // Different ways of constructing a TessLangModEdge TessLangModEdge(CubeRecoContext *cntxt, const Dawg *edge_array, EDGE_REF edge, int class_id); TessLangModEdge(CubeRecoContext *cntxt, const Dawg *edge_array, EDGE_REF start_edge_idx, EDGE_REF end_edge_idx, int class_id); TessLangModEdge(CubeRecoContext *cntxt, int class_id); ~TessLangModEdge() {} // Accessors inline bool IsRoot() const { return root_; } inline void SetRoot(bool flag) { root_ = flag; } inline bool IsOOD() const { return (dawg_ == (Dawg *)DAWG_OOD); } inline bool IsNumber() const { return (dawg_ == (Dawg *)DAWG_NUMBER); } inline bool IsEOW() const { return (IsTerminal() || (dawg_->end_of_word(end_edge_) != 0)); } inline const Dawg *GetDawg() const { return dawg_; } inline EDGE_REF StartEdge() const { return start_edge_; } inline EDGE_REF EndEdge() const { return end_edge_; } inline EDGE_REF EdgeMask() const { return edge_mask_; } inline const char_32 * EdgeString() const { return str_; } inline int ClassID () const { return class_id_; } inline int PathCost() const { return path_cost_; } inline void SetEdgeMask(EDGE_REF edge_mask) { edge_mask_ = edge_mask; } inline void SetDawg(Dawg *dawg) { dawg_ = dawg; } inline void SetStartEdge(EDGE_REF edge_idx) { start_edge_ = edge_idx; } inline void SetEndEdge(EDGE_REF edge_idx) { end_edge_ = edge_idx; } // is this a terminal node: // we can terminate at any OOD char, trailing punc or // when the dawg terminates inline bool IsTerminal() const { return (IsOOD() || IsNumber() || IsTrailingPuncEdge(start_edge_) || dawg_->next_node(end_edge_) == 0); } // How many signals does the LM provide for tuning. These are flags like: // OOD or not, Number of not that are used by the training to compute // extra costs for each word. inline int SignalCnt() const { return 2; } // returns the weight assigned to a specified signal inline double SignalWgt(int signal) const { CubeTuningParams *params = reinterpret_cast(cntxt_->Params()); if (params != NULL) { switch (signal) { case 0: return params->OODWgt(); break; case 1: return params->NumWgt(); break; } } return 0.0; } // sets the weight assigned to a specified signal: Used in training void SetSignalWgt(int signal, double wgt) { CubeTuningParams *params = reinterpret_cast(cntxt_->Params()); if (params != NULL) { switch (signal) { case 0: params->SetOODWgt(wgt); break; case 1: params->SetNumWgt(wgt); break; } } } // returns the actual value of a specified signal int Signal(int signal) { switch (signal) { case 0: return IsOOD() ? MIN_PROB_COST : 0; break; case 1: return IsNumber() ? MIN_PROB_COST : 0; break; default: return 0; } } // returns the Hash value of the edge. Used by the SearchNode hash table // to quickly lookup exisiting edges to converge during search inline unsigned int Hash() const { return static_cast( ((start_edge_ | end_edge_) ^ ((reinterpret_cast(dawg_)))) ^ ((unsigned int)edge_mask_) ^ class_id_); } // A verbal description of the edge: Used by visualizers char *Description() const; // Is this edge identical to the specified edge inline bool IsIdentical(LangModEdge *lang_mod_edge) const { return (class_id_ == reinterpret_cast(lang_mod_edge)->class_id_ && str_ == reinterpret_cast(lang_mod_edge)->str_ && dawg_ == reinterpret_cast(lang_mod_edge)->dawg_ && start_edge_ == reinterpret_cast(lang_mod_edge)->start_edge_ && end_edge_ == reinterpret_cast(lang_mod_edge)->end_edge_ && edge_mask_ == reinterpret_cast(lang_mod_edge)->edge_mask_); } // Creates a set of fan-out edges for the specified edge static int CreateChildren(CubeRecoContext *cntxt, const Dawg *edges, NODE_REF edge_reg, LangModEdge **lm_edges); private: bool root_; CubeRecoContext *cntxt_; const Dawg *dawg_; EDGE_REF start_edge_; EDGE_REF end_edge_; EDGE_REF edge_mask_; int path_cost_; int class_id_; const char_32 * str_; // returns the cost of the lang_mod_edge inline int Cost() const { if (cntxt_ != NULL) { CubeTuningParams *params = reinterpret_cast(cntxt_->Params()); if (dawg_ == (Dawg *)DAWG_OOD) { return static_cast(params->OODWgt() * MIN_PROB_COST); } else if (dawg_ == (Dawg *)DAWG_NUMBER) { return static_cast(params->NumWgt() * MIN_PROB_COST); } } return 0; } }; } // namespace tesseract #endif // TESS_LANG_MOD_EDGE_H tesseract-3.04.01/cube/tess_lang_model.cpp000066400000000000000000000431531266071204500204460ustar00rootroot00000000000000/********************************************************************** * File: tess_lang_model.cpp * Description: Implementation of the Tesseract Language Model Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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. * **********************************************************************/ // The TessLangModel class abstracts the Tesseract language model. It inherits // from the LangModel class. The Tesseract language model encompasses several // Dawgs (words from training data, punctuation, numbers, document words). // On top of this Cube adds an OOD state machine // The class provides methods to traverse the language model in a generative // fashion. Given any node in the DAWG, the language model can generate a list // of children (or fan-out) edges #include #include #include "char_samp.h" #include "cube_utils.h" #include "dict.h" #include "tesseractclass.h" #include "tess_lang_model.h" #include "tessdatamanager.h" #include "unicharset.h" namespace tesseract { // max fan-out (used for preallocation). Initialized here, but modified by // constructor int TessLangModel::max_edge_ = 4096; // Language model extra State machines const Dawg *TessLangModel::ood_dawg_ = reinterpret_cast(DAWG_OOD); const Dawg *TessLangModel::number_dawg_ = reinterpret_cast(DAWG_NUMBER); // number state machine const int TessLangModel::num_state_machine_[kStateCnt][kNumLiteralCnt] = { {0, 1, 1, NUM_TRM, NUM_TRM}, {NUM_TRM, 1, 1, 3, 2}, {NUM_TRM, NUM_TRM, 1, NUM_TRM, 2}, {NUM_TRM, NUM_TRM, 3, NUM_TRM, 2}, }; const int TessLangModel::num_max_repeat_[kStateCnt] = {3, 32, 8, 3}; // thresholds and penalties int TessLangModel::max_ood_shape_cost_ = CubeUtils::Prob2Cost(1e-4); TessLangModel::TessLangModel(const string &lm_params, const string &data_file_path, bool load_system_dawg, TessdataManager *tessdata_manager, CubeRecoContext *cntxt) { cntxt_ = cntxt; has_case_ = cntxt_->HasCase(); // Load the rest of the language model elements from file LoadLangModelElements(lm_params); // Load word_dawgs_ if needed. if (tessdata_manager->SeekToStart(TESSDATA_CUBE_UNICHARSET)) { word_dawgs_ = new DawgVector(); if (load_system_dawg && tessdata_manager->SeekToStart(TESSDATA_CUBE_SYSTEM_DAWG)) { // The last parameter to the Dawg constructor (the debug level) is set to // false, until Cube has a way to express its preferred debug level. *word_dawgs_ += new SquishedDawg(tessdata_manager->GetDataFilePtr(), DAWG_TYPE_WORD, cntxt_->Lang().c_str(), SYSTEM_DAWG_PERM, false); } } else { word_dawgs_ = NULL; } } // Cleanup an edge array void TessLangModel::FreeEdges(int edge_cnt, LangModEdge **edge_array) { if (edge_array != NULL) { for (int edge_idx = 0; edge_idx < edge_cnt; edge_idx++) { if (edge_array[edge_idx] != NULL) { delete edge_array[edge_idx]; } } delete []edge_array; } } // Determines if a sequence of 32-bit chars is valid in this language model // starting from the specified edge. If the eow_flag is ON, also checks for // a valid EndOfWord. If final_edge is not NULL, returns a pointer to the last // edge bool TessLangModel::IsValidSequence(LangModEdge *edge, const char_32 *sequence, bool eow_flag, LangModEdge **final_edge) { // get the edges emerging from this edge int edge_cnt = 0; LangModEdge **edge_array = GetEdges(NULL, edge, &edge_cnt); // find the 1st char in the sequence in the children for (int edge_idx = 0; edge_idx < edge_cnt; edge_idx++) { // found a match if (sequence[0] == edge_array[edge_idx]->EdgeString()[0]) { // if this is the last char if (sequence[1] == 0) { // succeed if we are in prefix mode or this is a terminal edge if (eow_flag == false || edge_array[edge_idx]->IsEOW()) { if (final_edge != NULL) { (*final_edge) = edge_array[edge_idx]; edge_array[edge_idx] = NULL; } FreeEdges(edge_cnt, edge_array); return true; } } else { // not the last char continue checking if (IsValidSequence(edge_array[edge_idx], sequence + 1, eow_flag, final_edge) == true) { FreeEdges(edge_cnt, edge_array); return true; } } } } FreeEdges(edge_cnt, edge_array); return false; } // Determines if a sequence of 32-bit chars is valid in this language model // starting from the root. If the eow_flag is ON, also checks for // a valid EndOfWord. If final_edge is not NULL, returns a pointer to the last // edge bool TessLangModel::IsValidSequence(const char_32 *sequence, bool eow_flag, LangModEdge **final_edge) { if (final_edge != NULL) { (*final_edge) = NULL; } return IsValidSequence(NULL, sequence, eow_flag, final_edge); } bool TessLangModel::IsLeadingPunc(const char_32 ch) { return lead_punc_.find(ch) != string::npos; } bool TessLangModel::IsTrailingPunc(const char_32 ch) { return trail_punc_.find(ch) != string::npos; } bool TessLangModel::IsDigit(const char_32 ch) { return digits_.find(ch) != string::npos; } // The general fan-out generation function. Returns the list of edges // fanning-out of the specified edge and their count. If an AltList is // specified, only the class-ids with a minimum cost are considered LangModEdge ** TessLangModel::GetEdges(CharAltList *alt_list, LangModEdge *lang_mod_edge, int *edge_cnt) { TessLangModEdge *tess_lm_edge = reinterpret_cast(lang_mod_edge); LangModEdge **edge_array = NULL; (*edge_cnt) = 0; // if we are starting from the root, we'll instantiate every DAWG // and get the all the edges that emerge from the root if (tess_lm_edge == NULL) { // get DAWG count from Tesseract int dawg_cnt = NumDawgs(); // preallocate the edge buffer (*edge_cnt) = dawg_cnt * max_edge_; edge_array = new LangModEdge *[(*edge_cnt)]; if (edge_array == NULL) { return NULL; } for (int dawg_idx = (*edge_cnt) = 0; dawg_idx < dawg_cnt; dawg_idx++) { const Dawg *curr_dawg = GetDawg(dawg_idx); // Only look through word Dawgs (since there is a special way of // handling numbers and punctuation). if (curr_dawg->type() == DAWG_TYPE_WORD) { (*edge_cnt) += FanOut(alt_list, curr_dawg, 0, 0, NULL, true, edge_array + (*edge_cnt)); } } // dawg (*edge_cnt) += FanOut(alt_list, number_dawg_, 0, 0, NULL, true, edge_array + (*edge_cnt)); // OOD: it is intentionally not added to the list to make sure it comes // at the end (*edge_cnt) += FanOut(alt_list, ood_dawg_, 0, 0, NULL, true, edge_array + (*edge_cnt)); // set the root flag for all root edges for (int edge_idx = 0; edge_idx < (*edge_cnt); edge_idx++) { edge_array[edge_idx]->SetRoot(true); } } else { // not starting at the root // preallocate the edge buffer (*edge_cnt) = max_edge_; // allocate memory for edges edge_array = new LangModEdge *[(*edge_cnt)]; if (edge_array == NULL) { return NULL; } // get the FanOut edges from the root of each dawg (*edge_cnt) = FanOut(alt_list, tess_lm_edge->GetDawg(), tess_lm_edge->EndEdge(), tess_lm_edge->EdgeMask(), tess_lm_edge->EdgeString(), false, edge_array); } return edge_array; } // generate edges from an NULL terminated string // (used for punctuation, operators and digits) int TessLangModel::Edges(const char *strng, const Dawg *dawg, EDGE_REF edge_ref, EDGE_REF edge_mask, LangModEdge **edge_array) { int edge_idx, edge_cnt = 0; for (edge_idx = 0; strng[edge_idx] != 0; edge_idx++) { int class_id = cntxt_->CharacterSet()->ClassID((char_32)strng[edge_idx]); if (class_id != INVALID_UNICHAR_ID) { // create an edge object edge_array[edge_cnt] = new TessLangModEdge(cntxt_, dawg, edge_ref, class_id); if (edge_array[edge_cnt] == NULL) { return 0; } reinterpret_cast(edge_array[edge_cnt])-> SetEdgeMask(edge_mask); edge_cnt++; } } return edge_cnt; } // generate OOD edges int TessLangModel::OODEdges(CharAltList *alt_list, EDGE_REF edge_ref, EDGE_REF edge_ref_mask, LangModEdge **edge_array) { int class_cnt = cntxt_->CharacterSet()->ClassCount(); int edge_cnt = 0; for (int class_id = 0; class_id < class_cnt; class_id++) { // produce an OOD edge only if the cost of the char is low enough if ((alt_list == NULL || alt_list->ClassCost(class_id) <= max_ood_shape_cost_)) { // create an edge object edge_array[edge_cnt] = new TessLangModEdge(cntxt_, class_id); if (edge_array[edge_cnt] == NULL) { return 0; } edge_cnt++; } } return edge_cnt; } // computes and returns the edges that fan out of an edge ref int TessLangModel::FanOut(CharAltList *alt_list, const Dawg *dawg, EDGE_REF edge_ref, EDGE_REF edge_mask, const char_32 *str, bool root_flag, LangModEdge **edge_array) { int edge_cnt = 0; NODE_REF next_node = NO_EDGE; // OOD if (dawg == reinterpret_cast(DAWG_OOD)) { if (ood_enabled_ == true) { return OODEdges(alt_list, edge_ref, edge_mask, edge_array); } else { return 0; } } else if (dawg == reinterpret_cast(DAWG_NUMBER)) { // Number if (numeric_enabled_ == true) { return NumberEdges(edge_ref, edge_array); } else { return 0; } } else if (IsTrailingPuncEdge(edge_mask)) { // a TRAILING PUNC MASK, generate more trailing punctuation and return if (punc_enabled_ == true) { EDGE_REF trail_cnt = TrailingPuncCount(edge_mask); return Edges(trail_punc_.c_str(), dawg, edge_ref, TrailingPuncEdgeMask(trail_cnt + 1), edge_array); } else { return 0; } } else if (root_flag == true || edge_ref == 0) { // Root, generate leading punctuation and continue if (root_flag) { if (punc_enabled_ == true) { edge_cnt += Edges(lead_punc_.c_str(), dawg, 0, LEAD_PUNC_EDGE_REF_MASK, edge_array); } } next_node = 0; } else { // a node in the main trie bool eow_flag = (dawg->end_of_word(edge_ref) != 0); // for EOW if (eow_flag == true) { // generate trailing punctuation if (punc_enabled_ == true) { edge_cnt += Edges(trail_punc_.c_str(), dawg, edge_ref, TrailingPuncEdgeMask((EDGE_REF)1), edge_array); // generate a hyphen and go back to the root edge_cnt += Edges("-/", dawg, 0, 0, edge_array + edge_cnt); } } // advance node next_node = dawg->next_node(edge_ref); if (next_node == 0 || next_node == NO_EDGE) { return edge_cnt; } } // now get all the emerging edges if word list is enabled if (word_list_enabled_ == true && next_node != NO_EDGE) { // create child edges int child_edge_cnt = TessLangModEdge::CreateChildren(cntxt_, dawg, next_node, edge_array + edge_cnt); int strt_cnt = edge_cnt; // set the edge mask for (int child = 0; child < child_edge_cnt; child++) { reinterpret_cast(edge_array[edge_cnt++])-> SetEdgeMask(edge_mask); } // if we are at the root, create upper case forms of these edges if possible if (root_flag == true) { for (int child = 0; child < child_edge_cnt; child++) { TessLangModEdge *child_edge = reinterpret_cast(edge_array[strt_cnt + child]); if (has_case_ == true) { const char_32 *edge_str = child_edge->EdgeString(); if (edge_str != NULL && islower(edge_str[0]) != 0 && edge_str[1] == 0) { int class_id = cntxt_->CharacterSet()->ClassID(toupper(edge_str[0])); if (class_id != INVALID_UNICHAR_ID) { // generate an upper case edge for lower case chars edge_array[edge_cnt] = new TessLangModEdge(cntxt_, dawg, child_edge->StartEdge(), child_edge->EndEdge(), class_id); if (edge_array[edge_cnt] != NULL) { reinterpret_cast(edge_array[edge_cnt])-> SetEdgeMask(edge_mask); edge_cnt++; } } } } } } } return edge_cnt; } // Generate the edges fanning-out from an edge in the number state machine int TessLangModel::NumberEdges(EDGE_REF edge_ref, LangModEdge **edge_array) { EDGE_REF new_state, state; inT64 repeat_cnt, new_repeat_cnt; state = ((edge_ref & NUMBER_STATE_MASK) >> NUMBER_STATE_SHIFT); repeat_cnt = ((edge_ref & NUMBER_REPEAT_MASK) >> NUMBER_REPEAT_SHIFT); if (state < 0 || state >= kStateCnt) { return 0; } // go through all valid transitions from the state int edge_cnt = 0; EDGE_REF new_edge_ref; for (int lit = 0; lit < kNumLiteralCnt; lit++) { // move to the new state new_state = num_state_machine_[state][lit]; if (new_state == NUM_TRM) { continue; } if (new_state == state) { new_repeat_cnt = repeat_cnt + 1; } else { new_repeat_cnt = 1; } // not allowed to repeat beyond this if (new_repeat_cnt > num_max_repeat_[state]) { continue; } new_edge_ref = (new_state << NUMBER_STATE_SHIFT) | (lit << NUMBER_LITERAL_SHIFT) | (new_repeat_cnt << NUMBER_REPEAT_SHIFT); edge_cnt += Edges(literal_str_[lit]->c_str(), number_dawg_, new_edge_ref, 0, edge_array + edge_cnt); } return edge_cnt; } // Loads Language model elements from contents of the .cube.lm file bool TessLangModel::LoadLangModelElements(const string &lm_params) { bool success = true; // split into lines, each corresponding to a token type below vector str_vec; CubeUtils::SplitStringUsing(lm_params, "\r\n", &str_vec); for (int entry = 0; entry < str_vec.size(); entry++) { vector tokens; // should be only two tokens: type and value CubeUtils::SplitStringUsing(str_vec[entry], "=", &tokens); if (tokens.size() != 2) success = false; if (tokens[0] == "LeadPunc") { lead_punc_ = tokens[1]; } else if (tokens[0] == "TrailPunc") { trail_punc_ = tokens[1]; } else if (tokens[0] == "NumLeadPunc") { num_lead_punc_ = tokens[1]; } else if (tokens[0] == "NumTrailPunc") { num_trail_punc_ = tokens[1]; } else if (tokens[0] == "Operators") { operators_ = tokens[1]; } else if (tokens[0] == "Digits") { digits_ = tokens[1]; } else if (tokens[0] == "Alphas") { alphas_ = tokens[1]; } else { success = false; } } RemoveInvalidCharacters(&num_lead_punc_); RemoveInvalidCharacters(&num_trail_punc_); RemoveInvalidCharacters(&digits_); RemoveInvalidCharacters(&operators_); RemoveInvalidCharacters(&alphas_); // form the array of literal strings needed for number state machine // It is essential that the literal strings go in the order below literal_str_[0] = &num_lead_punc_; literal_str_[1] = &num_trail_punc_; literal_str_[2] = &digits_; literal_str_[3] = &operators_; literal_str_[4] = &alphas_; return success; } void TessLangModel::RemoveInvalidCharacters(string *lm_str) { CharSet *char_set = cntxt_->CharacterSet(); tesseract::string_32 lm_str32; CubeUtils::UTF8ToUTF32(lm_str->c_str(), &lm_str32); int len = CubeUtils::StrLen(lm_str32.c_str()); char_32 *clean_str32 = new char_32[len + 1]; if (!clean_str32) return; int clean_len = 0; for (int i = 0; i < len; ++i) { int class_id = char_set->ClassID((char_32)lm_str32[i]); if (class_id != INVALID_UNICHAR_ID) { clean_str32[clean_len] = lm_str32[i]; ++clean_len; } } clean_str32[clean_len] = 0; if (clean_len < len) { lm_str->clear(); CubeUtils::UTF32ToUTF8(clean_str32, lm_str); } delete [] clean_str32; } int TessLangModel::NumDawgs() const { return (word_dawgs_ != NULL) ? word_dawgs_->size() : cntxt_->TesseractObject()->getDict().NumDawgs(); } // Returns the dawgs with the given index from either the dawgs // stored by the Tesseract object, or the word_dawgs_. const Dawg *TessLangModel::GetDawg(int index) const { if (word_dawgs_ != NULL) { ASSERT_HOST(index < word_dawgs_->size()); return (*word_dawgs_)[index]; } else { ASSERT_HOST(index < cntxt_->TesseractObject()->getDict().NumDawgs()); return cntxt_->TesseractObject()->getDict().GetDawg(index); } } } tesseract-3.04.01/cube/tess_lang_model.h000066400000000000000000000125611266071204500201120ustar00rootroot00000000000000/********************************************************************** * File: tess_lang_model.h * Description: Declaration of the Tesseract Language Model Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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 TESS_LANG_MODEL_H #define TESS_LANG_MODEL_H #include #include "char_altlist.h" #include "cube_reco_context.h" #include "cube_tuning_params.h" #include "dict.h" #include "lang_model.h" #include "tessdatamanager.h" #include "tess_lang_mod_edge.h" namespace tesseract { const int kStateCnt = 4; const int kNumLiteralCnt = 5; class TessLangModel : public LangModel { public: TessLangModel(const string &lm_params, const string &data_file_path, bool load_system_dawg, TessdataManager *tessdata_manager, CubeRecoContext *cntxt); ~TessLangModel() { if (word_dawgs_ != NULL) { word_dawgs_->delete_data_pointers(); delete word_dawgs_; } } // returns a pointer to the root of the language model inline TessLangModEdge *Root() { return NULL; } // The general fan-out generation function. Returns the list of edges // fanning-out of the specified edge and their count. If an AltList is // specified, only the class-ids with a minimum cost are considered LangModEdge **GetEdges(CharAltList *alt_list, LangModEdge *edge, int *edge_cnt); // Determines if a sequence of 32-bit chars is valid in this language model // starting from the root. If the eow_flag is ON, also checks for // a valid EndOfWord. If final_edge is not NULL, returns a pointer to the last // edge bool IsValidSequence(const char_32 *sequence, bool eow_flag, LangModEdge **final_edge = NULL); bool IsLeadingPunc(char_32 ch); bool IsTrailingPunc(char_32 ch); bool IsDigit(char_32 ch); void RemoveInvalidCharacters(string *lm_str); private: // static LM state machines static const Dawg *ood_dawg_; static const Dawg *number_dawg_; static const int num_state_machine_[kStateCnt][kNumLiteralCnt]; static const int num_max_repeat_[kStateCnt]; // word_dawgs_ should only be loaded if cube has its own version of the // unicharset (different from the one used by tesseract) and therefore // can not use the dawgs loaded for tesseract (since the unichar ids // encoded in the dawgs differ). DawgVector *word_dawgs_; static int max_edge_; static int max_ood_shape_cost_; // remaining language model elements needed by cube. These get loaded from // the .lm file string lead_punc_; string trail_punc_; string num_lead_punc_; string num_trail_punc_; string operators_; string digits_; string alphas_; // String of characters in RHS of each line of .cube.lm // Each element is hard-coded to correspond to a specific token type // (see LoadLangModelElements) string *literal_str_[kNumLiteralCnt]; // Recognition context needed to access language properties // (case, cursive,..) CubeRecoContext *cntxt_; bool has_case_; // computes and returns the edges that fan out of an edge ref int FanOut(CharAltList *alt_list, const Dawg *dawg, EDGE_REF edge_ref, EDGE_REF edge_ref_mask, const char_32 *str, bool root_flag, LangModEdge **edge_array); // generate edges from an NULL terminated string // (used for punctuation, operators and digits) int Edges(const char *strng, const Dawg *dawg, EDGE_REF edge_ref, EDGE_REF edge_ref_mask, LangModEdge **edge_array); // Generate the edges fanning-out from an edge in the number state machine int NumberEdges(EDGE_REF edge_ref, LangModEdge **edge_array); // Generate OOD edges int OODEdges(CharAltList *alt_list, EDGE_REF edge_ref, EDGE_REF edge_ref_mask, LangModEdge **edge_array); // Cleanup an edge array void FreeEdges(int edge_cnt, LangModEdge **edge_array); // Determines if a sequence of 32-bit chars is valid in this language model // starting from the specified edge. If the eow_flag is ON, also checks for // a valid EndOfWord. If final_edge is not NULL, returns a pointer to the last // edge bool IsValidSequence(LangModEdge *edge, const char_32 *sequence, bool eow_flag, LangModEdge **final_edge); // Parse language model elements from the given string, which should // have been loaded from .cube.lm file, e.g. in CubeRecoContext bool LoadLangModelElements(const string &lm_params); // Returns the number of word Dawgs in the language model. int NumDawgs() const; // Returns the dawgs with the given index from either the dawgs // stored by the Tesseract object, or the word_dawgs_. const Dawg *GetDawg(int index) const; }; } // tesseract #endif // TESS_LANG_MODEL_H tesseract-3.04.01/cube/tuning_params.h000066400000000000000000000120661266071204500176220ustar00rootroot00000000000000/********************************************************************** * File: tuning_params.h * Description: Declaration of the Tuning Parameters Base Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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. * **********************************************************************/ // The TuningParams class abstracts all the parameters that can be learned or // tuned during the training process. It is a base class that all TuningParams // classes should inherit from. #ifndef TUNING_PARAMS_H #define TUNING_PARAMS_H #include #ifdef USE_STD_NAMESPACE using std::string; #endif namespace tesseract { class TuningParams { public: enum type_classifer { NN, HYBRID_NN }; enum type_feature { BMP, CHEBYSHEV, HYBRID }; TuningParams() {} virtual ~TuningParams() {} // Accessor functions inline double RecoWgt() const { return reco_wgt_; } inline double SizeWgt() const { return size_wgt_; } inline double CharBigramWgt() const { return char_bigrams_wgt_; } inline double WordUnigramWgt() const { return word_unigrams_wgt_; } inline int MaxSegPerChar() const { return max_seg_per_char_; } inline int BeamWidth() const { return beam_width_; } inline int TypeClassifier() const { return tp_classifier_; } inline int TypeFeature() const { return tp_feat_; } inline int ConvGridSize() const { return conv_grid_size_; } inline int HistWindWid() const { return hist_wind_wid_; } inline int MinConCompSize() const { return min_con_comp_size_; } inline double MaxWordAspectRatio() const { return max_word_aspect_ratio_; } inline double MinSpaceHeightRatio() const { return min_space_height_ratio_; } inline double MaxSpaceHeightRatio() const { return max_space_height_ratio_; } inline double CombinerRunThresh() const { return combiner_run_thresh_; } inline double CombinerClassifierThresh() const { return combiner_classifier_thresh_; } inline void SetRecoWgt(double wgt) { reco_wgt_ = wgt; } inline void SetSizeWgt(double wgt) { size_wgt_ = wgt; } inline void SetCharBigramWgt(double wgt) { char_bigrams_wgt_ = wgt; } inline void SetWordUnigramWgt(double wgt) { word_unigrams_wgt_ = wgt; } inline void SetMaxSegPerChar(int max_seg_per_char) { max_seg_per_char_ = max_seg_per_char; } inline void SetBeamWidth(int beam_width) { beam_width_ = beam_width; } inline void SetTypeClassifier(type_classifer tp_classifier) { tp_classifier_ = tp_classifier; } inline void SetTypeFeature(type_feature tp_feat) {tp_feat_ = tp_feat;} inline void SetHistWindWid(int hist_wind_wid) { hist_wind_wid_ = hist_wind_wid; } virtual bool Save(string file_name) = 0; virtual bool Load(string file_name) = 0; protected: // weight of recognition cost. This includes the language model cost double reco_wgt_; // weight of size cost double size_wgt_; // weight of character bigrams cost double char_bigrams_wgt_; // weight of word unigrams cost double word_unigrams_wgt_; // Maximum number of segments per character int max_seg_per_char_; // Beam width equal to the maximum number of nodes kept in the beam search // trellis column after pruning int beam_width_; // Classifier type: See enum type_classifer for classifier types type_classifer tp_classifier_; // Feature types: See enum type_feature for feature types type_feature tp_feat_; // Grid size to scale a grapheme bitmap used by the BMP feature type int conv_grid_size_; // Histogram window size as a ratio of the word height used in computing // the vertical pixel density histogram in the segmentation algorithm int hist_wind_wid_; // Minimum possible size of a connected component int min_con_comp_size_; // Maximum aspect ratio of a word (width / height) double max_word_aspect_ratio_; // Minimum ratio relative to the line height of a gap to be considered as // a word break double min_space_height_ratio_; // Maximum ratio relative to the line height of a gap to be considered as // a definite word break double max_space_height_ratio_; // When Cube and Tesseract are run in combined mode, only run // combiner classifier when tesseract confidence is below this // threshold. When Cube is run without Tesseract, this is ignored. double combiner_run_thresh_; // When Cube and tesseract are run in combined mode, threshold on // output of combiner binary classifier (chosen from ROC during // combiner training). When Cube is run without Tesseract, this is ignored. double combiner_classifier_thresh_; }; } #endif // TUNING_PARAMS_H tesseract-3.04.01/cube/word_altlist.cpp000066400000000000000000000071501266071204500200130ustar00rootroot00000000000000/********************************************************************** * File: word_altlist.cpp * Description: Implementation of the Word Alternate List Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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 "word_altlist.h" namespace tesseract { WordAltList::WordAltList(int max_alt) : AltList(max_alt) { word_alt_ = NULL; } WordAltList::~WordAltList() { if (word_alt_ != NULL) { for (int alt_idx = 0; alt_idx < alt_cnt_; alt_idx++) { if (word_alt_[alt_idx] != NULL) { delete []word_alt_[alt_idx]; } } delete []word_alt_; word_alt_ = NULL; } } /** * insert an alternate word with the specified cost and tag */ bool WordAltList::Insert(char_32 *word_str, int cost, void *tag) { if (word_alt_ == NULL || alt_cost_ == NULL) { word_alt_ = new char_32*[max_alt_]; alt_cost_ = new int[max_alt_]; alt_tag_ = new void *[max_alt_]; if (word_alt_ == NULL || alt_cost_ == NULL || alt_tag_ == NULL) { return false; } memset(alt_tag_, 0, max_alt_ * sizeof(*alt_tag_)); } else { // check if alt already exists for (int alt_idx = 0; alt_idx < alt_cnt_; alt_idx++) { if (CubeUtils::StrCmp(word_str, word_alt_[alt_idx]) == 0) { // update the cost if we have a lower one if (cost < alt_cost_[alt_idx]) { alt_cost_[alt_idx] = cost; alt_tag_[alt_idx] = tag; } return true; } } } // determine length of alternate int len = CubeUtils::StrLen(word_str); word_alt_[alt_cnt_] = new char_32[len + 1]; if (word_alt_[alt_cnt_] == NULL) { return false; } if (len > 0) { memcpy(word_alt_[alt_cnt_], word_str, len * sizeof(*word_str)); } word_alt_[alt_cnt_][len] = 0; alt_cost_[alt_cnt_] = cost; alt_tag_[alt_cnt_] = tag; alt_cnt_++; return true; } /** * sort the alternate in descending order based on the cost */ void WordAltList::Sort() { for (int alt_idx = 0; alt_idx < alt_cnt_; alt_idx++) { for (int alt = alt_idx + 1; alt < alt_cnt_; alt++) { if (alt_cost_[alt_idx] > alt_cost_[alt]) { char_32 *pchTemp = word_alt_[alt_idx]; word_alt_[alt_idx] = word_alt_[alt]; word_alt_[alt] = pchTemp; int temp = alt_cost_[alt_idx]; alt_cost_[alt_idx] = alt_cost_[alt]; alt_cost_[alt] = temp; void *tag = alt_tag_[alt_idx]; alt_tag_[alt_idx] = alt_tag_[alt]; alt_tag_[alt] = tag; } } } } void WordAltList::PrintDebug() { for (int alt_idx = 0; alt_idx < alt_cnt_; alt_idx++) { char_32 *word_32 = word_alt_[alt_idx]; string word_str; CubeUtils::UTF32ToUTF8(word_32, &word_str); int num_unichars = CubeUtils::StrLen(word_32); fprintf(stderr, "Alt[%d]=%s (cost=%d, num_unichars=%d); unichars=", alt_idx, word_str.c_str(), alt_cost_[alt_idx], num_unichars); for (int i = 0; i < num_unichars; ++i) fprintf(stderr, "%d ", word_32[i]); fprintf(stderr, "\n"); } } } // namespace tesseract tesseract-3.04.01/cube/word_altlist.h000066400000000000000000000035151266071204500174610ustar00rootroot00000000000000/********************************************************************** * File: word_altlist.h * Description: Declaration of the Word Alternate List Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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. * **********************************************************************/ // The WordAltList abstracts a alternate list of words and their corresponding // costs that result from the word recognition process. The class inherits // from the AltList class // It provides methods to add a new word alternate, its corresponding score and // a tag. #ifndef WORD_ALT_LIST_H #define WORD_ALT_LIST_H #include "altlist.h" namespace tesseract { class WordAltList : public AltList { public: explicit WordAltList(int max_alt); ~WordAltList(); // Sort the list of alternates based on cost void Sort(); // insert an alternate word with the specified cost and tag bool Insert(char_32 *char_ptr, int cost, void *tag = NULL); // returns the alternate string at the specified position inline char_32 * Alt(int alt_idx) { return word_alt_[alt_idx]; } // print each entry of the altlist, both UTF8 and unichar ids, and // their costs, to stderr void PrintDebug(); private: char_32 **word_alt_; }; } // namespace tesseract #endif // WORD_ALT_LIST_H tesseract-3.04.01/cube/word_list_lang_model.cpp000066400000000000000000000143411266071204500214730ustar00rootroot00000000000000/********************************************************************** * File: word_list_lang_model.cpp * Description: Implementation of the Word List Language Model Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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 #include "word_list_lang_model.h" #include "cube_utils.h" #include "ratngs.h" #include "trie.h" namespace tesseract { WordListLangModel::WordListLangModel(CubeRecoContext *cntxt) { cntxt_ = cntxt; dawg_ = NULL; init_ = false; } WordListLangModel::~WordListLangModel() { Cleanup(); } // Cleanup void WordListLangModel::Cleanup() { if (dawg_ != NULL) { delete dawg_; dawg_ = NULL; } init_ = false; } // Initialize the language model bool WordListLangModel::Init() { if (init_ == true) { return true; } // The last parameter to the Trie constructor (the debug level) is set to // false for now, until Cube has a way to express its preferred debug level. dawg_ = new Trie(DAWG_TYPE_WORD, "", NO_PERM, cntxt_->CharacterSet()->ClassCount(), false); if (dawg_ == NULL) { return false; } init_ = true; return true; } // return a pointer to the root LangModEdge * WordListLangModel::Root() { return NULL; } // return the edges emerging from the current state LangModEdge **WordListLangModel::GetEdges(CharAltList *alt_list, LangModEdge *edge, int *edge_cnt) { // initialize if necessary if (init_ == false) { if (Init() == false) { return NULL; } } (*edge_cnt) = 0; EDGE_REF edge_ref; TessLangModEdge *tess_lm_edge = reinterpret_cast(edge); if (tess_lm_edge == NULL) { edge_ref = 0; } else { edge_ref = tess_lm_edge->EndEdge(); // advance node edge_ref = dawg_->next_node(edge_ref); if (edge_ref == 0) { return NULL; } } // allocate memory for edges LangModEdge **edge_array = new LangModEdge *[kMaxEdge]; if (edge_array == NULL) { return NULL; } // now get all the emerging edges (*edge_cnt) += TessLangModEdge::CreateChildren(cntxt_, dawg_, edge_ref, edge_array + (*edge_cnt)); return edge_array; } // returns true if the char_32 is supported by the language model // TODO(ahmadab) currently not implemented bool WordListLangModel::IsValidSequence(const char_32 *sequence, bool terminal, LangModEdge **edges) { return false; } // Recursive helper function for WordVariants(). void WordListLangModel::WordVariants(const CharSet &char_set, string_32 prefix_str32, WERD_CHOICE *word_so_far, string_32 str32, vector *word_variants) { int str_len = str32.length(); if (str_len == 0) { if (word_so_far->length() > 0) { word_variants->push_back(new WERD_CHOICE(*word_so_far)); } } else { // Try out all the possible prefixes of the str32. for (int len = 1; len <= str_len; len++) { // Check if prefix is supported in character set. string_32 str_pref32 = str32.substr(0, len); int class_id = char_set.ClassID(reinterpret_cast( str_pref32.c_str())); if (class_id <= 0) { continue; } else { string_32 new_prefix_str32 = prefix_str32 + str_pref32; string_32 new_str32 = str32.substr(len); word_so_far->append_unichar_id(class_id, 1, 0.0, 0.0); WordVariants(char_set, new_prefix_str32, word_so_far, new_str32, word_variants); word_so_far->remove_last_unichar_id(); } } } } // Compute all the variants of a 32-bit string in terms of the class-ids // This is needed for languages that have ligatures. A word can then have more // than one spelling in terms of the class-ids void WordListLangModel::WordVariants(const CharSet &char_set, const UNICHARSET *uchset, string_32 str32, vector *word_variants) { for (int i = 0; i < word_variants->size(); i++) { delete (*word_variants)[i]; } word_variants->clear(); string_32 prefix_str32; WERD_CHOICE word_so_far(uchset); WordVariants(char_set, prefix_str32, &word_so_far, str32, word_variants); } // add a new UTF-8 string to the lang model bool WordListLangModel::AddString(const char *char_ptr) { if (!init_ && !Init()) { // initialize if necessary return false; } string_32 str32; CubeUtils::UTF8ToUTF32(char_ptr, &str32); if (str32.length() < 1) { return false; } return AddString32(str32.c_str()); } // add a new UTF-32 string to the lang model bool WordListLangModel::AddString32(const char_32 *char_32_ptr) { if (char_32_ptr == NULL) { return false; } // get all the word variants vector word_variants; WordVariants(*(cntxt_->CharacterSet()), cntxt_->TessUnicharset(), char_32_ptr, &word_variants); if (word_variants.size() > 0) { // find the shortest variant int shortest_word = 0; for (int word = 1; word < word_variants.size(); word++) { if (word_variants[shortest_word]->length() > word_variants[word]->length()) { shortest_word = word; } } // only add the shortest grapheme interpretation of string to the word list dawg_->add_word_to_dawg(*word_variants[shortest_word]); } for (int i = 0; i < word_variants.size(); i++) { delete word_variants[i]; } return true; } } tesseract-3.04.01/cube/word_list_lang_model.h000066400000000000000000000067711266071204500211500ustar00rootroot00000000000000/********************************************************************** * File: word_list_lang_model.h * Description: Declaration of the Word List Language Model Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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. * **********************************************************************/ // The WordListLangModel class abstracts a language model that is based on // a list of words. It inherits from the LangModel abstract class // Besides providing the methods inherited from the LangModel abstract class, // the class provided methods to add new strings to the Language Model: // AddString & AddString32 #ifndef WORD_LIST_LANG_MODEL_H #define WORD_LIST_LANG_MODEL_H #include #include "cube_reco_context.h" #include "lang_model.h" #include "tess_lang_mod_edge.h" namespace tesseract { class Trie; class WordListLangModel : public LangModel { public: explicit WordListLangModel(CubeRecoContext *cntxt); ~WordListLangModel(); // Returns an edge pointer to the Root LangModEdge *Root(); // Returns the edges that fan-out of the specified edge and their count LangModEdge **GetEdges(CharAltList *alt_list, LangModEdge *edge, int *edge_cnt); // Returns is a sequence of 32-bit characters are valid within this language // model or net. And EndOfWord flag is specified. If true, the sequence has // to end on a valid word. The function also optionally returns the list // of language model edges traversed to parse the string bool IsValidSequence(const char_32 *sequence, bool eow_flag, LangModEdge **edges); bool IsLeadingPunc(char_32 ch) { return false; } // not yet implemented bool IsTrailingPunc(char_32 ch) { return false; } // not yet implemented bool IsDigit(char_32 ch) { return false; } // not yet implemented // Adds a new UTF-8 string to the language model bool AddString(const char *char_ptr); // Adds a new UTF-32 string to the language model bool AddString32(const char_32 *char_32_ptr); // Compute all the variants of a 32-bit string in terms of the class-ids. // This is needed for languages that have ligatures. A word can then have // more than one spelling in terms of the class-ids. static void WordVariants(const CharSet &char_set, const UNICHARSET *uchset, string_32 str32, vector *word_variants); private: // constants needed to configure the language model static const int kMaxEdge = 512; CubeRecoContext *cntxt_; Trie *dawg_; bool init_; // Initialize the language model bool Init(); // Cleanup void Cleanup(); // Recursive helper function for WordVariants(). static void WordVariants( const CharSet &char_set, string_32 prefix_str32, WERD_CHOICE *word_so_far, string_32 str32, vector *word_variants); }; } // tesseract #endif // WORD_LIST_LANG_MODEL_H tesseract-3.04.01/cube/word_size_model.cpp000066400000000000000000000256111266071204500204730ustar00rootroot00000000000000/********************************************************************** * File: word_size_model.cpp * Description: Implementation of the Word Size Model Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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 #include #include "word_size_model.h" #include "cube_utils.h" namespace tesseract { WordSizeModel::WordSizeModel(CharSet * char_set, bool contextual) { char_set_ = char_set; contextual_ = contextual; } WordSizeModel::~WordSizeModel() { for (int fnt = 0; fnt < font_pair_size_models_.size(); fnt++) { FontPairSizeInfo fnt_info = font_pair_size_models_[fnt]; delete []fnt_info.pair_size_info[0]; delete []fnt_info.pair_size_info; } } WordSizeModel *WordSizeModel::Create(const string &data_file_path, const string &lang, CharSet *char_set, bool contextual) { WordSizeModel *obj = new WordSizeModel(char_set, contextual); if (!obj) { fprintf(stderr, "Cube ERROR (WordSizeModel::Create): unable to allocate " "new word size model object\n"); return NULL; } if (!obj->Init(data_file_path, lang)) { delete obj; return NULL; } return obj; } bool WordSizeModel::Init(const string &data_file_path, const string &lang) { string stats_file_name; stats_file_name = data_file_path + lang; stats_file_name += ".cube.size"; // read file to memory string str_data; if (!CubeUtils::ReadFileToString(stats_file_name, &str_data)) { return false; } // split to words vector tokens; CubeUtils::SplitStringUsing(str_data, "\t\r\n", &tokens); if (tokens.size() < 1) { fprintf(stderr, "Cube ERROR (WordSizeModel::Init): invalid " "file contents: %s\n", stats_file_name.c_str()); return false; } font_pair_size_models_.clear(); // token count per line depends on whether the language is contextual or not int token_cnt = contextual_ ? (kExpectedTokenCount + 4) : kExpectedTokenCount; // the count of size classes depends on whether the language is contextual // or not. For non contextual languages (Ex: Eng), it is equal to the class // count. For contextual languages (Ex: Ara), it is equal to the class count // multiplied by the position count (4: start, middle, final, isolated) int size_class_cnt = contextual_ ? (char_set_->ClassCount() * 4) : char_set_->ClassCount(); string fnt_name = ""; for (int tok = 0; tok < tokens.size(); tok += token_cnt) { // a new font, write the old font data and re-init if (tok == 0 || fnt_name != tokens[tok]) { FontPairSizeInfo fnt_info; fnt_info.pair_size_info = new PairSizeInfo *[size_class_cnt]; if (!fnt_info.pair_size_info) { fprintf(stderr, "Cube ERROR (WordSizeModel::Init): error allcoating " "memory for font pair size info\n"); return false; } fnt_info.pair_size_info[0] = new PairSizeInfo[size_class_cnt * size_class_cnt]; if (!fnt_info.pair_size_info[0]) { fprintf(stderr, "Cube ERROR (WordSizeModel::Init): error allocating " "memory for font pair size info\n"); return false; } memset(fnt_info.pair_size_info[0], 0, size_class_cnt * size_class_cnt * sizeof(PairSizeInfo)); for (int cls = 1; cls < size_class_cnt; cls++) { fnt_info.pair_size_info[cls] = fnt_info.pair_size_info[cls - 1] + size_class_cnt; } // strip out path and extension string stripped_font_name = tokens[tok].substr(0, tokens[tok].find('.')); string::size_type strt_pos = stripped_font_name.find_last_of("/\\"); if (strt_pos != string::npos) { fnt_info.font_name = stripped_font_name.substr(strt_pos); } else { fnt_info.font_name = stripped_font_name; } font_pair_size_models_.push_back(fnt_info); } // parse the data int cls_0; int cls_1; double delta_top; double wid_0; double hgt_0; double wid_1; double hgt_1; int size_code_0; int size_code_1; // read and parse the tokens if (contextual_) { int start_0; int end_0; int start_1; int end_1; // The expected format for a character size bigram is as follows: // ClassId0Start-flag0End-flag0String0(ignored) // Width0Height0 // ClassId1Start-flag1End-flag1String1(ignored) // HeightDeltaWidth1Height0 // In case of non-contextual languages, the Start and End flags are // omitted if (sscanf(tokens[tok + 1].c_str(), "%d", &cls_0) != 1 || sscanf(tokens[tok + 2].c_str(), "%d", &start_0) != 1 || sscanf(tokens[tok + 3].c_str(), "%d", &end_0) != 1 || sscanf(tokens[tok + 5].c_str(), "%lf", &wid_0) != 1 || sscanf(tokens[tok + 6].c_str(), "%lf", &hgt_0) != 1 || sscanf(tokens[tok + 7].c_str(), "%d", &cls_1) != 1 || sscanf(tokens[tok + 8].c_str(), "%d", &start_1) != 1 || sscanf(tokens[tok + 9].c_str(), "%d", &end_1) != 1 || sscanf(tokens[tok + 11].c_str(), "%lf", &delta_top) != 1 || sscanf(tokens[tok + 12].c_str(), "%lf", &wid_1) != 1 || sscanf(tokens[tok + 13].c_str(), "%lf", &hgt_1) != 1 || (start_0 != 0 && start_0 != 1) || (end_0 != 0 && end_0 != 1) || (start_1 != 0 && start_1 != 1) || (end_1 != 0 && end_1 != 1)) { fprintf(stderr, "Cube ERROR (WordSizeModel::Init): bad format at " "line %d\n", 1 + (tok / token_cnt)); return false; } size_code_0 = SizeCode(cls_0, start_0, end_0); size_code_1 = SizeCode(cls_1, start_1, end_1); } else { if (sscanf(tokens[tok + 1].c_str(), "%d", &cls_0) != 1 || sscanf(tokens[tok + 3].c_str(), "%lf", &wid_0) != 1 || sscanf(tokens[tok + 4].c_str(), "%lf", &hgt_0) != 1 || sscanf(tokens[tok + 5].c_str(), "%d", &cls_1) != 1 || sscanf(tokens[tok + 7].c_str(), "%lf", &delta_top) != 1 || sscanf(tokens[tok + 8].c_str(), "%lf", &wid_1) != 1 || sscanf(tokens[tok + 9].c_str(), "%lf", &hgt_1) != 1) { fprintf(stderr, "Cube ERROR (WordSizeModel::Init): bad format at " "line %d\n", 1 + (tok / token_cnt)); return false; } size_code_0 = cls_0; size_code_1 = cls_1; } // copy the data to the size tables FontPairSizeInfo fnt_info = font_pair_size_models_.back(); fnt_info.pair_size_info[size_code_0][size_code_1].delta_top = static_cast(delta_top * kShapeModelScale); fnt_info.pair_size_info[size_code_0][size_code_1].wid_0 = static_cast(wid_0 * kShapeModelScale); fnt_info.pair_size_info[size_code_0][size_code_1].hgt_0 = static_cast(hgt_0 * kShapeModelScale); fnt_info.pair_size_info[size_code_0][size_code_1].wid_1 = static_cast(wid_1 * kShapeModelScale); fnt_info.pair_size_info[size_code_0][size_code_1].hgt_1 = static_cast(hgt_1 * kShapeModelScale); fnt_name = tokens[tok]; } return true; } int WordSizeModel::Cost(CharSamp **samp_array, int samp_cnt) const { if (samp_cnt < 2) { return 0; } double best_dist = static_cast(WORST_COST); int best_fnt = -1; for (int fnt = 0; fnt < font_pair_size_models_.size(); fnt++) { const FontPairSizeInfo *fnt_info = &font_pair_size_models_[fnt]; double mean_dist = 0; int pair_cnt = 0; for (int smp_0 = 0; smp_0 < samp_cnt; smp_0++) { int cls_0 = char_set_->ClassID(samp_array[smp_0]->StrLabel()); if (cls_0 < 1) { continue; } // compute size code for samp 0 based on class id and position int size_code_0; if (contextual_) { size_code_0 = SizeCode(cls_0, samp_array[smp_0]->FirstChar() == 0 ? 0 : 1, samp_array[smp_0]->LastChar() == 0 ? 0 : 1); } else { size_code_0 = cls_0; } int char0_height = samp_array[smp_0]->Height(); int char0_width = samp_array[smp_0]->Width(); int char0_top = samp_array[smp_0]->Top(); for (int smp_1 = smp_0 + 1; smp_1 < samp_cnt; smp_1++) { int cls_1 = char_set_->ClassID(samp_array[smp_1]->StrLabel()); if (cls_1 < 1) { continue; } // compute size code for samp 0 based on class id and position int size_code_1; if (contextual_) { size_code_1 = SizeCode(cls_1, samp_array[smp_1]->FirstChar() == 0 ? 0 : 1, samp_array[smp_1]->LastChar() == 0 ? 0 : 1); } else { size_code_1 = cls_1; } double dist = PairCost( char0_width, char0_height, char0_top, samp_array[smp_1]->Width(), samp_array[smp_1]->Height(), samp_array[smp_1]->Top(), fnt_info->pair_size_info[size_code_0][size_code_1]); if (dist > 0) { mean_dist += dist; pair_cnt++; } } // smp_1 } // smp_0 if (pair_cnt == 0) { continue; } mean_dist /= pair_cnt; if (best_fnt == -1 || mean_dist < best_dist) { best_dist = mean_dist; best_fnt = fnt; } } if (best_fnt == -1) { return static_cast(WORST_COST); } else { return static_cast(best_dist); } } double WordSizeModel::PairCost(int width_0, int height_0, int top_0, int width_1, int height_1, int top_1, const PairSizeInfo& pair_info) { double scale_factor = static_cast(pair_info.hgt_0) / static_cast(height_0); double dist = 0.0; if (scale_factor > 0) { double norm_width_0 = width_0 * scale_factor; double norm_width_1 = width_1 * scale_factor; double norm_height_1 = height_1 * scale_factor; double norm_delta_top = (top_1 - top_0) * scale_factor; // accumulate the distance between the model character and the // predicted one on all dimensions of the pair dist += fabs(pair_info.wid_0 - norm_width_0); dist += fabs(pair_info.wid_1 - norm_width_1); dist += fabs(pair_info.hgt_1 - norm_height_1); dist += fabs(pair_info.delta_top - norm_delta_top); } return dist; } } // namespace tesseract tesseract-3.04.01/cube/word_size_model.h000066400000000000000000000066461266071204500201470ustar00rootroot00000000000000/********************************************************************** * File: word_size_model.h * Description: Declaration of the Word Size Model Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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. * **********************************************************************/ // The WordSizeModel class abstracts the geometrical relationships // between characters/shapes in the same word (presumeably of the same font) // A non-parametric bigram model describes the three geometrical properties of a // character pair: // 1- Normalized Width // 2- Normalized Top // 3- Normalized Height // These dimensions are computed for each character pair in a word. These are // then compared to the same information for each of the fonts that the size // model knows about. The WordSizeCost is the cost of the font that matches // best. #ifndef WORD_SIZE_MODEL_H #define WORD_SIZE_MODEL_H #include #include "char_samp.h" #include "char_set.h" namespace tesseract { struct PairSizeInfo { int delta_top; int wid_0; int hgt_0; int wid_1; int hgt_1; }; struct FontPairSizeInfo { string font_name; PairSizeInfo **pair_size_info; }; class WordSizeModel { public: WordSizeModel(CharSet *, bool contextual); virtual ~WordSizeModel(); static WordSizeModel *Create(const string &data_file_path, const string &lang, CharSet *char_set, bool contextual); // Given a word and number of unichars, return the size cost, // minimized over all fonts in the size model. int Cost(CharSamp **samp_array, int samp_cnt) const; // Given dimensions of a pair of character samples and a font size // model for that character pair, return the pair's size cost for // the font. static double PairCost(int width_0, int height_0, int top_0, int width_1, int height_1, int top_1, const PairSizeInfo& pair_info); bool Save(string file_name); // Number of fonts in size model. inline int FontCount() const { return font_pair_size_models_.size(); } inline const FontPairSizeInfo *FontInfo() const { return &font_pair_size_models_[0]; } // Helper functions to convert between size codes, class id and position // codes static inline int SizeCode(int cls_id, int start, int end) { return (cls_id << 2) + (end << 1) + start; } private: // Scaling constant used to convert floating point ratios in size table // to fixed point static const int kShapeModelScale = 1000; static const int kExpectedTokenCount = 10; // Language properties bool contextual_; CharSet *char_set_; // Size ratios table vector font_pair_size_models_; // Initialize the word size model object bool Init(const string &data_file_path, const string &lang); }; } #endif // WORD_SIZE_MODEL_H tesseract-3.04.01/cube/word_unigrams.cpp000066400000000000000000000203021266071204500201560ustar00rootroot00000000000000/********************************************************************** * File: word_unigrams.cpp * Description: Implementation of the Word Unigrams Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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 #include #include #include "const.h" #include "cube_utils.h" #include "ndminx.h" #include "word_unigrams.h" namespace tesseract { WordUnigrams::WordUnigrams() { costs_ = NULL; words_ = NULL; word_cnt_ = 0; } WordUnigrams::~WordUnigrams() { if (words_ != NULL) { if (words_[0] != NULL) { delete []words_[0]; } delete []words_; words_ = NULL; } if (costs_ != NULL) { delete []costs_; } } /** * Load the word-list and unigrams from file and create an object * The word list is assumed to be sorted in lexicographic order. */ WordUnigrams *WordUnigrams::Create(const string &data_file_path, const string &lang) { string file_name; string str; file_name = data_file_path + lang; file_name += ".cube.word-freq"; // load the string into memory if (CubeUtils::ReadFileToString(file_name, &str) == false) { return NULL; } // split into lines vector str_vec; CubeUtils::SplitStringUsing(str, "\r\n \t", &str_vec); if (str_vec.size() < 2) { return NULL; } // allocate memory WordUnigrams *word_unigrams_obj = new WordUnigrams(); if (word_unigrams_obj == NULL) { fprintf(stderr, "Cube ERROR (WordUnigrams::Create): could not create " "word unigrams object.\n"); return NULL; } int full_len = str.length(); int word_cnt = str_vec.size() / 2; word_unigrams_obj->words_ = new char*[word_cnt]; word_unigrams_obj->costs_ = new int[word_cnt]; if (word_unigrams_obj->words_ == NULL || word_unigrams_obj->costs_ == NULL) { fprintf(stderr, "Cube ERROR (WordUnigrams::Create): error allocating " "word unigram fields.\n"); delete word_unigrams_obj; return NULL; } word_unigrams_obj->words_[0] = new char[full_len]; if (word_unigrams_obj->words_[0] == NULL) { fprintf(stderr, "Cube ERROR (WordUnigrams::Create): error allocating " "word unigram fields.\n"); delete word_unigrams_obj; return NULL; } // construct sorted list of words and costs word_unigrams_obj->word_cnt_ = 0; char *char_buff = word_unigrams_obj->words_[0]; word_cnt = 0; int max_cost = 0; for (int wrd = 0; wrd < str_vec.size(); wrd += 2) { word_unigrams_obj->words_[word_cnt] = char_buff; strcpy(char_buff, str_vec[wrd].c_str()); char_buff += (str_vec[wrd].length() + 1); if (sscanf(str_vec[wrd + 1].c_str(), "%d", word_unigrams_obj->costs_ + word_cnt) != 1) { fprintf(stderr, "Cube ERROR (WordUnigrams::Create): error reading " "word unigram data.\n"); delete word_unigrams_obj; return NULL; } // update max cost max_cost = MAX(max_cost, word_unigrams_obj->costs_[word_cnt]); word_cnt++; } word_unigrams_obj->word_cnt_ = word_cnt; // compute the not-in-list-cost by assuming that a word not in the list // [ahmadab]: This can be computed as follows: // - Given that the distribution of words follow Zipf's law: // (F = K / (rank ^ S)), where s is slightly > 1.0 // - Number of words in the list is N // - The mean frequency of a word that did not appear in the list is the // area under the rest of the Zipf's curve divided by 2 (the mean) // - The area would be the bound integral from N to infinity = // (K * S) / (N ^ (S + 1)) ~= K / (N ^ 2) // - Given that cost = -LOG(prob), the cost of an unlisted word would be // = max_cost + 2*LOG(N) word_unigrams_obj->not_in_list_cost_ = max_cost + (2 * CubeUtils::Prob2Cost(1.0 / word_cnt)); // success return word_unigrams_obj; } /** * Split input into space-separated tokens, strip trailing punctuation * from each, determine case properties, call UTF-8 flavor of cost * function on each word, and aggregate all into single mean word * cost. */ int WordUnigrams::Cost(const char_32 *key_str32, LangModel *lang_mod, CharSet *char_set) const { if (!key_str32) return 0; // convert string to UTF8 to split into space-separated words string key_str; CubeUtils::UTF32ToUTF8(key_str32, &key_str); vector words; CubeUtils::SplitStringUsing(key_str, " \t", &words); // no words => no cost if (words.size() <= 0) { return 0; } // aggregate the costs of all the words int cost = 0; for (int word_idx = 0; word_idx < words.size(); word_idx++) { // convert each word back to UTF32 for analyzing case and punctuation string_32 str32; CubeUtils::UTF8ToUTF32(words[word_idx].c_str(), &str32); int len = CubeUtils::StrLen(str32.c_str()); // strip all trailing punctuation string clean_str; int clean_len = len; bool trunc = false; while (clean_len > 0 && lang_mod->IsTrailingPunc(str32.c_str()[clean_len - 1])) { --clean_len; trunc = true; } // If either the original string was not truncated (no trailing // punctuation) or the entire string was removed (all characters // are trailing punctuation), evaluate original word as is; // otherwise, copy all but the trailing punctuation characters char_32 *clean_str32 = NULL; if (clean_len == 0 || !trunc) { clean_str32 = CubeUtils::StrDup(str32.c_str()); } else { clean_str32 = new char_32[clean_len + 1]; for (int i = 0; i < clean_len; ++i) { clean_str32[i] = str32[i]; } clean_str32[clean_len] = '\0'; } ASSERT_HOST(clean_str32 != NULL); string str8; CubeUtils::UTF32ToUTF8(clean_str32, &str8); int word_cost = CostInternal(str8.c_str()); // if case invariant, get costs of all-upper-case and all-lower-case // versions and return the min cost if (clean_len >= kMinLengthNumOrCaseInvariant && CubeUtils::IsCaseInvariant(clean_str32, char_set)) { char_32 *lower_32 = CubeUtils::ToLower(clean_str32, char_set); if (lower_32) { string lower_8; CubeUtils::UTF32ToUTF8(lower_32, &lower_8); word_cost = MIN(word_cost, CostInternal(lower_8.c_str())); delete [] lower_32; } char_32 *upper_32 = CubeUtils::ToUpper(clean_str32, char_set); if (upper_32) { string upper_8; CubeUtils::UTF32ToUTF8(upper_32, &upper_8); word_cost = MIN(word_cost, CostInternal(upper_8.c_str())); delete [] upper_32; } } if (clean_len >= kMinLengthNumOrCaseInvariant) { // if characters are all numeric, incur 0 word cost bool is_numeric = true; for (int i = 0; i < clean_len; ++i) { if (!lang_mod->IsDigit(clean_str32[i])) is_numeric = false; } if (is_numeric) word_cost = 0; } delete [] clean_str32; cost += word_cost; } // word_idx // return the mean cost return static_cast(cost / static_cast(words.size())); } /** * Search for UTF-8 string using binary search of sorted words_ array. */ int WordUnigrams::CostInternal(const char *key_str) const { if (strlen(key_str) == 0) return not_in_list_cost_; int hi = word_cnt_ - 1; int lo = 0; while (lo <= hi) { int current = (hi + lo) / 2; int comp = strcmp(key_str, words_[current]); // a match if (comp == 0) { return costs_[current]; } if (comp < 0) { // go lower hi = current - 1; } else { // go higher lo = current + 1; } } return not_in_list_cost_; } } // namespace tesseract tesseract-3.04.01/cube/word_unigrams.h000066400000000000000000000052631266071204500176340ustar00rootroot00000000000000 /********************************************************************** * File: word_unigrams.h * Description: Declaration of the Word Unigrams Class * Author: Ahmad Abdulkader * Created: 2008 * * (C) Copyright 2008, 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. * **********************************************************************/ // The WordUnigram class holds the unigrams of the most frequent set of words // in a language. It is an optional component of the Cube OCR engine. If // present, the unigram cost of a word is aggregated with the other costs // (Recognition, Language Model, Size) to compute a cost for a word. // The word list is assumed to be sorted in lexicographic order. #ifndef WORD_UNIGRAMS_H #define WORD_UNIGRAMS_H #include #include "char_set.h" #include "lang_model.h" namespace tesseract { class WordUnigrams { public: WordUnigrams(); ~WordUnigrams(); // Load the word-list and unigrams from file and create an object // The word list is assumed to be sorted static WordUnigrams *Create(const string &data_file_path, const string &lang); // Compute the unigram cost of a UTF-32 string. Splits into // space-separated tokens, strips trailing punctuation from each // token, evaluates case properties, and calls internal Cost() // function on UTF-8 version. To avoid unnecessarily penalizing // all-one-case words or capitalized words (first-letter // upper-case and remaining letters lower-case) when not all // versions of the word appear in the .cube.word-freq file, a // case-invariant cost is computed in those cases, assuming the word // meets a minimum length. int Cost(const char_32 *str32, LangModel *lang_mod, CharSet *char_set) const; protected: // Compute the word unigram cost of a UTF-8 string with binary // search of sorted words_ array. int CostInternal(const char *str) const; private: // Only words this length or greater qualify for all-numeric or // case-invariant word unigram cost. static const int kMinLengthNumOrCaseInvariant = 4; int word_cnt_; char **words_; int *costs_; int not_in_list_cost_; }; } #endif // WORD_UNIGRAMS_H tesseract-3.04.01/cutil/000077500000000000000000000000001266071204500147775ustar00rootroot00000000000000tesseract-3.04.01/cutil/Makefile.am000066400000000000000000000014561266071204500170410ustar00rootroot00000000000000AM_CPPFLAGS += -I$(top_srcdir)/ccutil -I$(top_srcdir)/viewer if VISIBILITY AM_CPPFLAGS += -DTESS_EXPORTS \ -fvisibility=hidden -fvisibility-inlines-hidden 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 \ oldlist.h structures.h if !USING_MULTIPLELIBS noinst_LTLIBRARIES = libtesseract_cutil.la else lib_LTLIBRARIES = libtesseract_cutil.la libtesseract_cutil_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) libtesseract_cutil_la_LIBADD = \ ../ccutil/libtesseract_ccutil.la \ ../viewer/libtesseract_viewer.la endif libtesseract_cutil_la_SOURCES = \ bitvec.cpp callcpp.cpp cutil.cpp cutil_class.cpp danerror.cpp efio.cpp \ emalloc.cpp freelist.cpp listio.cpp \ oldlist.cpp structures.cpp tesseract-3.04.01/cutil/Makefile.in000066400000000000000000000553371266071204500170610ustar00rootroot00000000000000# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @VISIBILITY_TRUE@am__append_1 = -DTESS_EXPORTS \ @VISIBILITY_TRUE@ -fvisibility=hidden -fvisibility-inlines-hidden subdir = cutil DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/config/depcomp $(noinst_HEADERS) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config_auto.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) @USING_MULTIPLELIBS_TRUE@libtesseract_cutil_la_DEPENDENCIES = \ @USING_MULTIPLELIBS_TRUE@ ../ccutil/libtesseract_ccutil.la \ @USING_MULTIPLELIBS_TRUE@ ../viewer/libtesseract_viewer.la am_libtesseract_cutil_la_OBJECTS = bitvec.lo callcpp.lo cutil.lo \ cutil_class.lo danerror.lo efio.lo emalloc.lo freelist.lo \ listio.lo oldlist.lo structures.lo libtesseract_cutil_la_OBJECTS = $(am_libtesseract_cutil_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libtesseract_cutil_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ $(AM_CXXFLAGS) $(CXXFLAGS) $(libtesseract_cutil_la_LDFLAGS) \ $(LDFLAGS) -o $@ @USING_MULTIPLELIBS_FALSE@am_libtesseract_cutil_la_rpath = @USING_MULTIPLELIBS_TRUE@am_libtesseract_cutil_la_rpath = -rpath \ @USING_MULTIPLELIBS_TRUE@ $(libdir) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__depfiles_maybe = depfiles am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(libtesseract_cutil_la_SOURCES) DIST_SOURCES = $(libtesseract_cutil_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_CPPFLAGS = @AM_CPPFLAGS@ -I$(top_srcdir)/ccutil \ -I$(top_srcdir)/viewer $(am__append_1) AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AM_LDFLAGS = @AM_LDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FRAMEWORK_OPENCL = @FRAMEWORK_OPENCL@ GENERIC_API_VERSION = @GENERIC_API_VERSION@ GENERIC_LIBRARY_NAME = @GENERIC_LIBRARY_NAME@ GENERIC_LIBRARY_VERSION = @GENERIC_LIBRARY_VERSION@ GENERIC_MAJOR_VERSION = @GENERIC_MAJOR_VERSION@ GENERIC_RELEASE = @GENERIC_RELEASE@ GENERIC_VERSION = @GENERIC_VERSION@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBLEPT_HEADERSDIR = @LIBLEPT_HEADERSDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCL_CPPFLAGS = @OPENCL_CPPFLAGS@ OPENCL_LDFLAGS = @OPENCL_LDFLAGS@ OPENMP_CXXFLAGS = @OPENMP_CXXFLAGS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_DATE = @PACKAGE_DATE@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_YEAR = @PACKAGE_YEAR@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ 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 \ oldlist.h structures.h @USING_MULTIPLELIBS_FALSE@noinst_LTLIBRARIES = libtesseract_cutil.la @USING_MULTIPLELIBS_TRUE@lib_LTLIBRARIES = libtesseract_cutil.la @USING_MULTIPLELIBS_TRUE@libtesseract_cutil_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) @USING_MULTIPLELIBS_TRUE@libtesseract_cutil_la_LIBADD = \ @USING_MULTIPLELIBS_TRUE@ ../ccutil/libtesseract_ccutil.la \ @USING_MULTIPLELIBS_TRUE@ ../viewer/libtesseract_viewer.la libtesseract_cutil_la_SOURCES = \ bitvec.cpp callcpp.cpp cutil.cpp cutil_class.cpp danerror.cpp efio.cpp \ emalloc.cpp freelist.cpp listio.cpp \ oldlist.cpp structures.cpp all: all-am .SUFFIXES: .SUFFIXES: .cpp .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign cutil/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign cutil/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libtesseract_cutil.la: $(libtesseract_cutil_la_OBJECTS) $(libtesseract_cutil_la_DEPENDENCIES) $(EXTRA_libtesseract_cutil_la_DEPENDENCIES) $(AM_V_CXXLD)$(libtesseract_cutil_la_LINK) $(am_libtesseract_cutil_la_rpath) $(libtesseract_cutil_la_OBJECTS) $(libtesseract_cutil_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bitvec.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callcpp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cutil.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cutil_class.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/danerror.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/efio.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emalloc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/freelist.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/listio.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oldlist.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/structures.Plo@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ clean-noinstLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-libLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-libLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libLTLIBRARIES clean-libtool clean-noinstLTLIBRARIES \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-libLTLIBRARIES \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-libLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: tesseract-3.04.01/cutil/bitvec.cpp000066400000000000000000000063331266071204500167640ustar00rootroot00000000000000/****************************************************************************** ** Filename: bitvec.c ** Purpose: Routines for manipulating bit vectors ** Author: Dan Johnson ** History: Thu Mar 15 10:37:27 1990, 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 "bitvec.h" #include #include "emalloc.h" #include "freelist.h" #include "tprintf.h" /*----------------------------------------------------------------------------- Public Code -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /** * This routine uses realloc to increase the size of * the specified bit vector. * * Globals: * - none * * @param Vector bit vector to be expanded * @param NewNumBits new size of bit vector * * @return New expanded bit vector. * @note Exceptions: none * @note History: Fri Nov 16 10:11:16 1990, DSJ, Created. */ BIT_VECTOR ExpandBitVector(BIT_VECTOR Vector, int NewNumBits) { return ((BIT_VECTOR) Erealloc(Vector, sizeof(Vector[0]) * WordsInVectorOfSize(NewNumBits))); } /* ExpandBitVector */ /*---------------------------------------------------------------------------*/ void FreeBitVector(BIT_VECTOR BitVector) { /** * This routine frees a bit vector. It also decrements * the global counter that keeps track of the number of * bit vectors allocated. If BitVector is NULL, then * the count is printed to stderr. * * Globals: * - BitVectorCount count of number of bit vectors allocated * * @param BitVector bit vector to be freed * * @note Exceptions: none * @note History: Tue Oct 23 16:46:09 1990, DSJ, Created. */ if (BitVector) { Efree(BitVector); } } /* FreeBitVector */ /*---------------------------------------------------------------------------*/ /** * Allocate and return a new bit vector large enough to * hold the specified number of bits. * * Globals: * - BitVectorCount number of bit vectors allocated * * @param NumBits number of bits in new bit vector * * @return New bit vector. * @note Exceptions: none * @note History: Tue Oct 23 16:51:27 1990, DSJ, Created. */ BIT_VECTOR NewBitVector(int NumBits) { return ((BIT_VECTOR) Emalloc(sizeof(uinT32) * WordsInVectorOfSize(NumBits))); } /* NewBitVector */ tesseract-3.04.01/cutil/bitvec.h000066400000000000000000000055061266071204500164320ustar00rootroot00000000000000/****************************************************************************** ** Filename: bitvec.h ** Purpose: Routines for manipulating bit vectors ** Author: Dan Johnson ** History: Wed Mar 7 17:52:45 1990, 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 BITVEC_H #define BITVEC_H #include "host.h" /*----------------------------------------------------------------------------- Include Files and Type Defines -----------------------------------------------------------------------------*/ // TODO(rays) Rename BITSINLONG to BITSINuinT32, and use sizeof. #define BITSINLONG 32 /**< no of bits in a long */ typedef uinT32 *BIT_VECTOR; /*----------------------------------------------------------------------------- Public Function Prototypes -----------------------------------------------------------------------------*/ #define zero_all_bits(array,length) \ {\ int index; /*temporary index*/\ \ for (index=0;index #include #endif #include #include "memry.h" #include "scrollview.h" #include "params.h" #include "callcpp.h" #include "tprintf.h" #include "host.h" #include "unichar.h" void cprintf ( //Trace printf const char *format, ... //special message ) { va_list args; //variable args char msg[1000]; va_start(args, format); //variable list vsprintf(msg, format, args); //Format into msg va_end(args); tprintf ("%s", msg); } #ifndef GRAPHICS_DISABLED ScrollView *c_create_window( /*create a window */ const char *name, /*name/title of window */ inT16 xpos, /*coords of window */ inT16 ypos, /*coords of window */ inT16 xsize, /*size of window */ inT16 ysize, /*size of window */ double xmin, /*scrolling limits */ double xmax, /*to stop users */ double ymin, /*getting lost in */ double ymax /*empty space */ ) { return new ScrollView(name, xpos, ypos, xsize, ysize, xmax - xmin, ymax - ymin, true); } void c_line_color_index( /*set color */ void *win, C_COL index) { // The colors are the same as the SV ones except that SV has COLOR:NONE --> offset of 1 ScrollView* window = (ScrollView*) win; window->Pen((ScrollView::Color) (index + 1)); } void c_move( /*move pen */ void *win, double x, double y) { ScrollView* window = (ScrollView*) win; window->SetCursor((int) x, (int) y); } void c_draw( /*move pen */ void *win, double x, double y) { ScrollView* window = (ScrollView*) win; window->DrawTo((int) x, (int) y); } void c_make_current( /*move pen */ void *win) { ScrollView* window = (ScrollView*) win; window->Update(); } void c_clear_window( /*move pen */ void *win) { ScrollView* window = (ScrollView*) win; window->Clear(); } char window_wait(ScrollView* win) { SVEvent* ev; // Wait till an input or click event (all others are thrown away) char ret = '\0'; SVEventType ev_type = SVET_ANY; do { ev = win->AwaitEvent(SVET_ANY); ev_type = ev->type; if (ev_type == SVET_INPUT) ret = ev->parameter[0]; delete ev; } while (ev_type != SVET_INPUT && ev_type != SVET_CLICK); return ret; } #endif void reverse32(void *ptr) { char tmp; char *cptr = (char *) ptr; tmp = *cptr; *cptr = *(cptr + 3); *(cptr + 3) = tmp; tmp = *(cptr + 1); *(cptr + 1) = *(cptr + 2); *(cptr + 2) = tmp; } void reverse16(void *ptr) { char tmp; char *cptr = (char *) ptr; tmp = *cptr; *cptr = *(cptr + 1); *(cptr + 1) = tmp; } tesseract-3.04.01/cutil/callcpp.h000066400000000000000000000056661266071204500166030ustar00rootroot00000000000000/********************************************************************** * File: callcpp.h * Description: extern C interface calling C++ from C. * Author: Ray Smith * Created: Sun Feb 04 20:39:23 MST 1996 * * (C) Copyright 1996, Hewlett-Packard Co. ** 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 CALLCPP_H #define CALLCPP_H #ifndef __UNIX__ #include #endif #include "host.h" #include "params.h" #include "unichar.h" class ScrollView; typedef enum { Black, White, Red, Yellow, Green, Cyan, Blue, Magenta, Aquamarine, Dark_SLATE_BLUE, Light_BLUE, Medium_BLUE, Midnight_BLUE, Navy_BLUE, Sky_BLUE, Slate_BLUE, Steel_BLUE, Coral, Brown, Sandy_BROWN, Gold, GoldENROD, Dark_GREEN, Dark_OLIVE_GREEN, Forest_GREEN, Lime_GREEN, Pale_GREEN, Yellow_GREEN, Light_GREY, Dark_SLATE_GREY, Dim_GREY, Grey, Khaki, Maroon, Orange, Orchid, Pink, Plum, Indian_RED, Orange_RED, Violet_RED, Salmon, Tan, Turqoise, Dark_TURQUOISE, Violet, Wheat, Green_YELLOW } C_COL; /*starbase colours */ void cprintf ( //Trace printf const char *format, ... //special message ); ScrollView *c_create_window( /*create a window */ const char *name, /*name/title of window */ inT16 xpos, /*coords of window */ inT16 ypos, /*coords of window */ inT16 xsize, /*size of window */ inT16 ysize, /*size of window */ double xmin, /*scrolling limits */ double xmax, /*to stop users */ double ymin, /*getting lost in */ double ymax /*empty space */ ); void c_line_color_index( /*set color */ void *win, C_COL index); void c_move( /*move pen */ void *win, double x, double y); void c_draw( /*move pen */ void *win, double x, double y); void c_make_current( /*move pen */ void *win); void c_clear_window( /*move pen */ void *win); char window_wait(ScrollView* win); void reverse32(void *ptr); void reverse16(void *ptr); #endif tesseract-3.04.01/cutil/const.h000066400000000000000000000017331266071204500163020ustar00rootroot00000000000000/************************************************************************** ** 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 CONST_H #define CONST_H /*This file contains constants which are global to the entire system*/ #define SPLINESIZE 23 // max spline parts to a line #define PI 3.14159265359 // pi #define EDGEPTFLAGS 4 // concavity,length etc. #endif tesseract-3.04.01/cutil/cutil.cpp000066400000000000000000000061101266071204500166210ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: cutil.c * Description: General utility functions * Author: Mark Seaman, SW Productivity * Created: Fri Oct 16 14:37:00 1987 * Modified: Wed Jun 6 16:29:17 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. * ******************************************************************************** Revision 1.1 2007/02/02 23:39:07 theraysmith Fixed portability issues Revision 1.1.1.1 2004/02/20 19:39:06 slumos Import original HP distribution * Revision 1.3 90/03/06 15:39:10 15:39:10 marks (Mark Seaman) * Look for correct file of or * * Revision 1.2 90/01/15 13:02:13 13:02:13 marks (Mark Seaman) * Added memory allocator (*allocate) and (*deallocate) * * Revision 1.1 89/10/09 14:58:29 14:58:29 marks (Mark Seaman) * Initial revision **/ #include "cutil.h" #include "tprintf.h" #include "callcpp.h" #include #define RESET_COUNT 2000 /********************************************************************** * long_rand * * Return a long random number whose value is less than limit. Do this * by calling the standard cheepo random number generator and reseting * it pretty often. **********************************************************************/ long long_rand(long limit) { #if RAND_MAX < 0x1000000 static long seed; long num; num = (long) rand () << 16; num |= rand () & 0xffff; seed ^= num; long result = num % limit; while (result < 0) { result += limit; } return result; #else return (long)((double)limit * rand()/(RAND_MAX + 1.0)); #endif } /********************************************************************** * open_file * * Open a file for reading or writing. If the file name parameter is * NULL use stdin (or stdout) for the file. If the file can not be * opened then call the error routine. **********************************************************************/ FILE *open_file(const char *filename, const char *mode) { FILE *thisfile = NULL; if ((thisfile = fopen (filename, mode)) == NULL) { tprintf ("Could not open file, %s\n", filename); exit (1); } return (thisfile); } /// Check whether the file exists bool exists_file(const char *filename) { bool exists = false; FILE *f = NULL; if ((f = fopen(filename, "rb")) != NULL) { fclose(f); exists = true; } return exists; } tesseract-3.04.01/cutil/cutil.h000066400000000000000000000077321266071204500163010ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: cutil.h * Description: General utility functions * Author: Mark Seaman, SW Productivity * Created: Fri Oct 16 14:37:00 1987 * Modified: Wed Dec 5 15:40:26 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. * ******************************************************************************** Revision 1.1 2007/02/02 23:39:07 theraysmith Fixed portability issues Revision 1.1.1.1 2004/02/20 19:39:06 slumos Import original HP distribution */ #ifndef CUTILH #define CUTILH /*---------------------------------------------------------------------- I n c l u d e s ----------------------------------------------------------------------*/ #include #include #include #include "host.h" #include "tprintf.h" /*---------------------------------------------------------------------- T y p e s ----------------------------------------------------------------------*/ #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define CHARS_PER_LINE 500 #if defined(__STDC__) || defined(__cplusplus) || MAC_OR_DOS # define _ARGS(s) s #else # define _ARGS(s) () #endif //typedef int (*int_proc) (void); typedef void (*void_proc) (...); typedef void *(*void_star_proc) _ARGS ((...)); typedef int (*int_void) (void); typedef void (*void_void) (void); typedef int (*int_compare) (void *, void *); typedef void (*void_dest) (void *); /*---------------------------------------------------------------------- M a c r o s ----------------------------------------------------------------------*/ /********************************************************************** * new_line * * Print a new line character on stdout. **********************************************************************/ #define new_line() \ tprintf("\n") /********************************************************************** * print_string * * Print a string on stdout. **********************************************************************/ #define print_string(str) \ printf ("%s\n", str) /********************************************************************** * strfree * * Reserve a spot in memory for the string to be stored. Copy the string * to it and return the result. **********************************************************************/ #define strfree(s) (free_string(s)) /********************************************************************** * strsave * * Reserve a spot in memory for the string to be stored. Copy the string * to it and return the result. **********************************************************************/ #define strsave(s) \ ((s) != NULL ? \ ((char*) strcpy (alloc_string(strlen(s)+1), s)) : \ (NULL)) /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ long long_rand(long limit); FILE *open_file(const char *filename, const char *mode); bool exists_file(const char *filename); /* util.c long long_rand _ARGS ((long limit)); FILE *open_file _ARGS((char *filename, char *mode)); #undef _ARGS */ #include "cutil_class.h" #endif tesseract-3.04.01/cutil/cutil_class.cpp000066400000000000000000000016451266071204500200160ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: cutil.cpp // Description: cutil class. // Author: Samuel Charron // // (C) Copyright 2006, 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 "cutil_class.h" namespace tesseract { CUtil::CUtil() { } CUtil::~CUtil() { } } // namespace tesseract tesseract-3.04.01/cutil/cutil_class.h000066400000000000000000000022321266071204500174540ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: cutil.h // Description: cutil class. // Author: Samuel Charron // // (C) Copyright 2006, 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_CUTIL_CUTIL_CLASS_H__ #define TESSERACT_CUTIL_CUTIL_CLASS_H__ #include "ccutil.h" #include "const.h" #include "strngs.h" namespace tesseract { class CUtil : public CCUtil { public: CUtil(); ~CUtil(); void read_variables(const char *filename, bool global_only); }; } // namespace tesseract #endif // TESSERACT_CUTIL_CUTIL_CLASS_H__ tesseract-3.04.01/cutil/danerror.cpp000066400000000000000000000035041266071204500173210ustar00rootroot00000000000000/****************************************************************************** ** Filename: danerror.c ** Purpose: Routines for managing error trapping ** Author: Dan Johnson ** History: 3/17/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 "host.h" #include "danerror.h" #include "tprintf.h" #include "globaloc.h" #ifdef __UNIX__ #include "assert.h" #endif #include /** * This routine prints the specified error message to stderr. * It then jumps to the current error trap. If the error trap * stack is empty, the calling program is terminated with a * fatal error message. * * @param Error error number which is to be trapped * @param Message pointer to a string to be printed as an error message * @return None - this routine does not return. * @note History: 4/3/89, DSJ, Created. */ void DoError(int Error, const char *Message) { if (Message != NULL) { tprintf("\nError: %s!\n", Message); } err_exit(); } /* DoError */ tesseract-3.04.01/cutil/danerror.h000066400000000000000000000027421266071204500167710ustar00rootroot00000000000000/****************************************************************************** ** Filename: danerror.h ** Purpose: Definition of error trapping routines. ** Author: Dan Johnson ** History: 4/3/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 DANERROR_H #define DANERROR_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #define NOERROR 0 #define DO_NOTHING 0 typedef int TRAPERROR; typedef void (*VOID_PROC) (); /**---------------------------------------------------------------------------- Public Function Prototypes ----------------------------------------------------------------------------**/ void DoError(int Error, const char *Message); #endif tesseract-3.04.01/cutil/efio.cpp000066400000000000000000000042051266071204500164260ustar00rootroot00000000000000/****************************************************************************** ** Filename: efio.c ** Purpose: Utility I/O routines ** Author: Dan Johnson ** History: 5/21/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 "efio.h" #include "danerror.h" #include #include #define MAXERRORMESSAGE 256 /*---------------------------------------------------------------------------- Public Code ----------------------------------------------------------------------------*/ /** * This routine attempts to open the specified file in the * specified mode. If the file can be opened, a pointer to * the open file is returned. If the file cannot be opened, * an error is trapped. * @param Name name of file to be opened * @param Mode mode to be used to open file * @return Pointer to open file. * @note Globals: None * @note Exceptions: #FOPENERROR unable to open specified file * @note History: 5/21/89, DSJ, Created. */ FILE *Efopen(const char *Name, const char *Mode) { FILE *File; char ErrorMessage[MAXERRORMESSAGE]; File = fopen (Name, Mode); if (File == NULL) { sprintf (ErrorMessage, "Unable to open %s", Name); DoError(FOPENERROR, ErrorMessage); return (NULL); } else return (File); } /* Efopen */ tesseract-3.04.01/cutil/efio.h000066400000000000000000000026331266071204500160760ustar00rootroot00000000000000/****************************************************************************** ** Filename: efio.h ** Purpose: Definition of file I/O routines ** Author: Dan Johnson ** History: 5/21/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 EFIO_H #define EFIO_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include #define FOPENERROR 3000 /**---------------------------------------------------------------------------- Public Function Prototype ----------------------------------------------------------------------------**/ FILE *Efopen(const char *Name, const char *Mode); #endif tesseract-3.04.01/cutil/emalloc.cpp000066400000000000000000000057271266071204500171320ustar00rootroot00000000000000/************************************************************************** * Filename: emalloc.c ** Purpose: Routines for trapping memory allocation errors. ** Author: Dan Johnson HP-UX 6.2 HP-UX 6.2 ** History: 4/3/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 "emalloc.h" #include "danerror.h" #include /*---------------------------------------------------------------------------- Public Code ----------------------------------------------------------------------------*/ /** * This routine attempts to allocate the specified number of * bytes. If the memory can be allocated, a pointer to the * memory is returned. If the memory cannot be allocated, or * if the allocation request is negative or zero, * an error is trapped. * @param Size number of bytes of memory to be allocated * @return Pointer to allocated memory. * @note Exceptions: * - #NOTENOUGHMEMORY unable to allocate Size bytes * - #ILLEGALMALLOCREQUEST negative or zero request size * @note History: 4/3/89, DSJ, Created. */ void *Emalloc(int Size) { void *Buffer; if (Size <= 0) DoError (ILLEGALMALLOCREQUEST, "Illegal malloc request size"); Buffer = (void *) malloc (Size); if (Buffer == NULL) { DoError (NOTENOUGHMEMORY, "Not enough memory"); return (NULL); } else return (Buffer); } /* Emalloc */ /*---------------------------------------------------------------------------*/ void *Erealloc(void *ptr, int size) { void *Buffer; if (size < 0 || (size == 0 && ptr == NULL)) DoError (ILLEGALMALLOCREQUEST, "Illegal realloc request size"); Buffer = (void *) realloc (ptr, size); if (Buffer == NULL && size != 0) DoError (NOTENOUGHMEMORY, "Not enough memory"); return (Buffer); } /* Erealloc */ /*---------------------------------------------------------------------------*/ void Efree(void *ptr) { if (ptr == NULL) DoError (ILLEGALMALLOCREQUEST, "Attempted to free NULL ptr"); free(ptr); } /* Efree */ tesseract-3.04.01/cutil/emalloc.h000066400000000000000000000033371266071204500165720ustar00rootroot00000000000000/****************************************************************************** ** Filename: emalloc.h ** Purpose: Definition of memory allocation routines. ** Author: Dan Johnson ** History: 4/3/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 EMALLOC_H #define EMALLOC_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "host.h" #include "callcpp.h" #define NOTENOUGHMEMORY 2000 #define ILLEGALMALLOCREQUEST 2001 /**---------------------------------------------------------------------------- Public Function Prototypes ----------------------------------------------------------------------------**/ void *Emalloc(int Size); void *Erealloc(void *ptr, int size); void Efree(void *ptr); /**---------------------------------------------------------------------------- Global Data Definitions and Declarations ----------------------------------------------------------------------------**/ #endif tesseract-3.04.01/cutil/freelist.cpp000066400000000000000000000022371266071204500173240ustar00rootroot00000000000000/************************************************************************** ** 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 "freelist.h" #include // With improvements in OS memory allocators, internal memory management is // no longer required, so these functions all map to their malloc-family // equivalents. int *memalloc(int size) { return static_cast(malloc(static_cast(size))); } int *memrealloc(void *ptr, int size, int oldsize) { return static_cast(realloc(ptr, static_cast(size))); } void memfree(void *element) { free(element); } tesseract-3.04.01/cutil/freelist.h000066400000000000000000000031571266071204500167730ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: freelist.h (Formerly freelist.h) * Description: Memory allocator * Author: Mark Seaman, OCR Technology * Created: Wed May 30 13:50:28 1990 * Modified: Mon Dec 10 15:15:25 1990 (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 FREELIST_H #define FREELIST_H /*---------------------------------------------------------------------- I n c l u d e s ----------------------------------------------------------------------*/ #include /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ int *memalloc(int size); int *memrealloc(void *ptr, int size, int oldsize); void memfree(void *element); #endif tesseract-3.04.01/cutil/globals.h000066400000000000000000000023521266071204500165750ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: globals.h (Formerly globals.h) * Description: Global Variables for Wise Owl * Author: Mark Seaman, OCR Technology * Created: Thu Dec 21 11:38:36 1989 * Modified: Thu Jan 4 17:13:00 1990 (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 GLOBALS_H #define GLOBALS_H #include "const.h" #include "unicharset.h" #include "strngs.h" #include #endif tesseract-3.04.01/cutil/listio.cpp000066400000000000000000000044661266071204500170200ustar00rootroot00000000000000/* -*-C-*- ################################################################################ # # File: listio.c # Description: List I/O processing procedures. # Author: Mark Seaman, Software Productivity # Created: Thu Jul 23 13:24:09 1987 # Modified: Fri May 17 17:33:30 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 implementations of a set of general purpose list I/O routines. For the interface definitions look in the file "listio.h". ---------------------------------------------------------------------------*/ #include #include #include #include "listio.h" /*--------------------------------------------------------------------------- Public Function Code ---------------------------------------------------------------------------*/ /************************************************************************* * R E A D L I S T * * Read a list of strings from a file. Return the string list to the * caller. *************************************************************************/ LIST read_list(const char *filename) { FILE *infile; char s[CHARS_PER_LINE]; LIST list; if ((infile = open_file (filename, "r")) == NULL) return (NIL_LIST); list = NIL_LIST; while (fgets (s, CHARS_PER_LINE, infile) != NULL) { s[CHARS_PER_LINE - 1] = '\0'; if (strlen (s) > 0) { if (s[strlen (s) - 1] == '\n') s[strlen (s) - 1] = '\0'; if (strlen (s) > 0) { list = push (list, (LIST) strsave (s)); } } } fclose(infile); return (reverse_d (list)); } tesseract-3.04.01/cutil/listio.h000066400000000000000000000031771266071204500164630ustar00rootroot00000000000000/* -*-C-*- ################################################################################ # # File: listio.h # Description: List I/O processing procedures. # Author: Mark Seaman, Software Productivity # Created: Thu Jul 23 13:24:09 1987 # Modified: Mon Oct 16 11:38:52 1989 (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. # ################################################################################ * Revision 1.5 89/06/27 11:56:00 11:56:00 marks (Mark Seaman) * Fixed MAC_OR_DOS bug * This file contains the interface definitions to a set of general purpose list I/O routines. ***********************************************************************/ #ifndef LISTIO_H #define LISTIO_H #include #include "oldlist.h" /*---------------------------------------------------------------------------- Public Function Prototypes --------------------------------------------------------------------------*/ LIST read_list(const char *filename); #endif tesseract-3.04.01/cutil/oldlist.cpp000066400000000000000000000322121266071204500171550ustar00rootroot00000000000000/* -*-C-*- ############################################################################### # # File: list.c # Description: List processing procedures. # Author: Mark Seaman, Software Productivity # Created: Thu Jul 23 13:24:09 1987 # Modified: Thu Dec 22 10:59:52 1988 (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. # ################################################################################ * Revision 1.13 90/03/06 15:37:54 15:37:54 marks (Mark Seaman) * Look for correct file of or * * Revision 1.12 90/02/26 17:37:36 17:37:36 marks (Mark Seaman) * Added pop_off and join_on * This file contains a set of general purpose list manipulation routines. These routines can be used in a wide variety of ways to provide several different popular data structures. A new list can be created by declaring a variable of type 'LIST', and can be initialized with the value 'NIL_LIST'. All of these routines check for the NIL_LIST condition before dereferencing pointers. NOTE: There is a users' manual available in printed form from Mark Seaman at (303) 350-4492 at Greeley Hard Copy. To implement a STACK use: push to add to the Stack l = push (l, (LIST) "jim"); pop to remove items from the Stack l = pop (l); first_node to access the head name = (char *) first_node (l); To implement a QUEUE use: push_last to add to the Queue l = push_last (l, (LIST) "jim"); pop remove items from the Queue l = pop (l); first_node to access the head name = (char *) first_node (l); To implement LISP like functions use: first_node CAR x = (int) first_node (l); rest CDR l = list_rest (l); push CONS l = push (l, (LIST) this); last LAST x = last (l); concat APPEND l = concat (r, s); count LENGTH x = count (l); search MEMBER if (search (l, x, NULL)) To implement SETS use: adjoin l = adjoin (l, x); set_union l = set_union (r, s); intersection l = intersection (r, s); set_difference l = set_difference (r, s); delete l = delete (s, x, NULL); search if (search (l, x, NULL)) To Implement Associated LISTS use: lpush l = lpush (l, p); assoc s = assoc (l, x); adelete l = adelete (l, x); The following rules of closure exist for the functions provided. a = first_node (push (a, b)) b = list_rest (push (a, b)) a = push (pop (a), a)) For all a <> NIL_LIST a = reverse (reverse (a)) ******************************************************************************/ #include "oldlist.h" #include "structures.h" #include #if MAC_OR_DOS #include #else #include "freelist.h" #endif /*---------------------------------------------------------------------- M a c r o s ----------------------------------------------------------------------*/ #define add_on(l,x) l = push (l,first_node (x)) #define next_one(l) l = list_rest (l) /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ /********************************************************************** * c o u n t * * Recursively count the elements in a list. Return the count. **********************************************************************/ int count(LIST var_list) { int temp = 0; iterate (var_list) temp += 1; return (temp); } /********************************************************************** * d e l e t e d * * Delete all the elements out of the current list that match the key. * This operation destroys the original list. The caller will supply a * routine that will compare each node to the * key, and return a non-zero value when they match. If the value * NULL is supplied for is_equal, the is_key routine will be used. **********************************************************************/ LIST delete_d(LIST list, void *key, int_compare is_equal) { LIST result = NIL_LIST; LIST last_one = NIL_LIST; if (is_equal == NULL) is_equal = is_same; while (list != NIL_LIST) { if (!(*is_equal) (first_node (list), key)) { if (last_one == NIL_LIST) { last_one = list; list = list_rest (list); result = last_one; set_rest(last_one, NIL_LIST); } else { set_rest(last_one, list); last_one = list; list = list_rest (list); set_rest(last_one, NIL_LIST); } } else { list = pop (list); } } return (result); } LIST delete_d(LIST list, void *key, TessResultCallback2* is_equal) { LIST result = NIL_LIST; LIST last_one = NIL_LIST; while (list != NIL_LIST) { if (!(*is_equal).Run (first_node (list), key)) { if (last_one == NIL_LIST) { last_one = list; list = list_rest (list); result = last_one; set_rest(last_one, NIL_LIST); } else { set_rest(last_one, list); last_one = list; list = list_rest (list); set_rest(last_one, NIL_LIST); } } else { list = pop (list); } } return (result); } /********************************************************************** * d e s t r o y * * Return the space taken by a list to the heap. **********************************************************************/ LIST destroy(LIST list) { LIST next; while (list != NIL_LIST) { next = list_rest (list); free_cell(list); list = next; } return (NIL_LIST); } /********************************************************************** * d e s t r o y n o d e s * * Return the space taken by the LISTs of a list to the heap. **********************************************************************/ void destroy_nodes(LIST list, void_dest destructor) { if (destructor == NULL) destructor = memfree; while (list != NIL_LIST) { (*destructor) (first_node (list)); list = pop (list); } } /********************************************************************** * i n s e r t * * Create a list element and rearange the pointers so that the first * element in the list is the second aurgment. **********************************************************************/ void insert(LIST list, void *node) { LIST element; if (list != NIL_LIST) { element = push (NIL_LIST, node); set_rest (element, list_rest (list)); set_rest(list, element); node = first_node (list); list->node = first_node (list_rest (list)); list->next->node = (LIST) node; } } /********************************************************************** * i s s a m e n o d e * * Compare the list node with the key value return TRUE (non-zero) * if they are equivalent strings. (Return FALSE if not) **********************************************************************/ int is_same_node(void *item1, void *item2) { return (item1 == item2); } /********************************************************************** * i s s a m e * * Compare the list node with the key value return TRUE (non-zero) * if they are equivalent strings. (Return FALSE if not) **********************************************************************/ int is_same(void *item1, void *item2) { return (!strcmp ((char *) item1, (char *) item2)); } /********************************************************************** * j o i n * * Join the two lists together. This function is similar to concat * except that concat creates a new list. This function returns the * first list updated. **********************************************************************/ LIST join(LIST list1, LIST list2) { if (list1 == NIL_LIST) return (list2); set_rest (last (list1), list2); return (list1); } /********************************************************************** * l a s t * * Return the last list item (this is list type). **********************************************************************/ LIST last(LIST var_list) { while (list_rest (var_list) != NIL_LIST) var_list = list_rest (var_list); return (var_list); } /********************************************************************** * n t h c e l l * * Return nth list cell in the list. **********************************************************************/ void *nth_cell(LIST var_list, int item_num) { int x = 0; iterate(var_list) { if (x++ == item_num) return (var_list); } return (var_list); } /********************************************************************** * p o p * * Return the list with the first element removed. Destroy the space * that it occupied in the list. **********************************************************************/ LIST pop(LIST list) { LIST temp; temp = list_rest (list); if (list != NIL_LIST) { free_cell(list); } return (temp); } /********************************************************************** * p u s h * * Create a list element. Push the second parameter (the node) onto * the first parameter (the list). Return the new list to the caller. **********************************************************************/ LIST push(LIST list, void *element) { LIST t; t = new_cell (); t->node = (LIST) element; set_rest(t, list); return (t); } /********************************************************************** * p u s h l a s t * * Create a list element. Add the element onto the end of the list. **********************************************************************/ LIST push_last(LIST list, void *item) { LIST t; if (list != NIL_LIST) { t = last (list); t->next = push (NIL_LIST, item); return (list); } else return (push (NIL_LIST, item)); } /********************************************************************** * r e v e r s e * * Create a new list with the elements reversed. The old list is not * destroyed. **********************************************************************/ LIST reverse(LIST list) { LIST newlist = NIL_LIST; iterate (list) copy_first (list, newlist); return (newlist); } /********************************************************************** * r e v e r s e d * * Create a new list with the elements reversed. The old list is * destroyed. **********************************************************************/ LIST reverse_d(LIST list) { LIST result = reverse (list); destroy(list); return (result); } /********************************************************************** * s a d j o i n * * Adjoin an element to an assorted list. The original list is * modified. Returns the modified list. **********************************************************************/ LIST s_adjoin(LIST var_list, void *variable, int_compare compare) { LIST l; int result; if (compare == NULL) compare = (int_compare) strcmp; l = var_list; iterate(l) { result = (*compare) (variable, first_node (l)); if (result == 0) return (var_list); else if (result < 0) { insert(l, variable); return (var_list); } } return (push_last (var_list, variable)); } /********************************************************************** * s e a r c h * * Search list, return NIL_LIST if not found. Return the list starting from * the item if found. The compare routine "is_equal" is passed in as * the third parameter to this routine. If the value NULL is supplied * for is_equal, the is_key routine will be used. **********************************************************************/ LIST search(LIST list, void *key, int_compare is_equal) { if (is_equal == NULL) is_equal = is_same; iterate (list) if ((*is_equal) (first_node (list), key)) return (list); return (NIL_LIST); } LIST search(LIST list, void *key, TessResultCallback2* is_equal) { iterate (list) if ((*is_equal).Run(first_node (list), key)) return (list); return (NIL_LIST); } tesseract-3.04.01/cutil/oldlist.h000066400000000000000000000256741266071204500166400ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: list.h (Formerly list.h) * Description: List processing procedures declarations. * Author: Mark Seaman, SW Productivity * Created: Fri Oct 16 14:37:00 1987 * Modified: Wed Dec 5 15:43:17 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 the interface for a set of general purpose list * manipulation routines. For the implementation of these routines see * the file "list.c". * ******************************************************************************** * * INDEX * ======= * * BASICS: * ------- * first_node - Macro to return the first list node (not the cell). * list_rest - Macro the return the second list cell * pop - Destroy one list cell * push - Create one list cell and set the node and next fields * * ITERATION: * ----------------- * iterate - Macro to create a for loop to visit each cell. * iterate_list - Macro to visit each cell using a local variable. * for_each - Applies a function to each node. * * LIST CELL COUNTS: * ----------------- * count - Returns the number of list cells in the list. * second_node - Returns the second node. * third - Returns the third node. * fourth - Returns the fourth node. * fifth - Returns the fifth node. * last - Returns the last list cell. * pair - Creates a list of two elements. * * COPYING: * ----------------- * copy_first - Pushes the first element from list 1 onto list 2. * copy - Create a copy of a list. * concat - Creates a new list that is a copy of both input lists. * delete_n - Creates a new list without the chosen elements. * reverse - Creates a backwards copy of the input list. * sort - Use quick sort to construct a new list. * transform - Creates a new list by transforming each of the nodes. * * TRANFORMS: (Note: These functions all modify the input list.) * ---------- * join - Concatenates list 1 and list 2. * delete_d - Removes the requested elements from the list. * transform_d - Modifies the list by applying a function to each node. * insert - Add a new element into this spot in a list. (not NIL_LIST) * push_last - Add a new element onto the end of a list. * reverse_d - Reverse a list and destroy the old one. * * ASSOCIATED LISTS: * ----------------- * adelete - Remove a particular entry from an associated list. * assoc - Find an entry in an associated list that matches a key. * match - Return the data element of an a-list entry. * * DISPLAY: * ----------------- * print_cell - Print a hex dump of a list cell. * show - Displays a string and a list (using lprint). * * SETS: * ----- * adjoin - Add a new element to list if it does not exist already. * intersection - Create a new list that is the set intersection. * set_union - Create a new list that is the set intersection. * set_difference - Create a new list that is the set difference. * s_adjoin - Add an element to a sort list if it is not there. * s_intersection - Set intersection on a sorted list. Modifies old list. * s_union - Set intersection on a sorted list. Modifies old list. * search - Return the pointer to the list cell whose node matches. * * COMPARISONS: * ----------------- * is_same - Compares each node to the key. * is_not_same - Compares each node to the key. * is_key - Compares first of each node to the key. * is_not_key - Compares first of each node to the key. * * CELL OPERATIONS: * ----------------- * new_cell - Obtain a new list cell from the free list. Allocate. * free_cell - Return a list cell to the free list. * destroy - Return all list cells in a list. * destroy_nodes - Apply a function to each list cell and destroy the list. * set_node - Assign the node field in a list cell. * set_rest - Assign the next field in a list cell. * ***********************************************************************/ #ifndef LIST_H #define LIST_H #include "cutil.h" #include "tesscallback.h" /*---------------------------------------------------------------------- T y p e s ----------------------------------------------------------------------*/ #define NIL_LIST (LIST) 0 struct list_rec { struct list_rec *node; struct list_rec *next; }; typedef list_rec *LIST; /*---------------------------------------------------------------------- M a c r o s ----------------------------------------------------------------------*/ /* Predefinitions */ #define list_rest(l) ((l) ? (l)->next : NIL_LIST) #define first_node(l) ((l) ? (l)->node : NIL_LIST) /********************************************************************** * c o p y f i r s t * * Do the appropriate kind a push operation to copy the first node from * one list to another. * **********************************************************************/ #define copy_first(l1,l2) \ (l2=push(l2, first_node(l1))) /********************************************************************** * i t e r a t e * * Visit each node in the list. Replace the old list with the list * minus the head. Continue until the list is NIL_LIST. **********************************************************************/ #define iterate(l) \ for (; (l) != NIL_LIST; (l) = list_rest (l)) /********************************************************************** * i t e r a t e l i s t * * Visit each node in the list (l). Use a local variable (x) to iterate * through all of the list cells. This macro is identical to iterate * except that it does not lose the original list. **********************************************************************/ #define iterate_list(x,l) \ for ((x)=(l); (x)!=0; (x)=list_rest(x)) /********************************************************************** * j o i n o n * * Add another list onto the tail of this one. The list given as an input * parameter is modified. **********************************************************************/ #define JOIN_ON(list1,list2) \ ((list1) = join ((list1), (list2))) /********************************************************************** * p o p o f f * * Add a cell onto the front of a list. The list given as an input * parameter is modified. **********************************************************************/ #define pop_off(list) \ ((list) = pop (list)) /********************************************************************** * p u s h o n * * Add a cell onto the front of a list. The list given as an input * parameter is modified. **********************************************************************/ #define push_on(list,thing) \ ((list) = push (list, (LIST) (thing))) /********************************************************************** * s e c o n d * * Return the contents of the second list element. * * #define second_node(l) first_node (list_rest (l)) **********************************************************************/ #define second_node(l) \ first_node (list_rest (l)) /********************************************************************** * s e t r e s t * * Change the "next" field of a list element to point to a desired place. * * #define set_rest(l,node) l->next = node; **********************************************************************/ #define set_rest(l,cell)\ ((l)->next = (cell)) /********************************************************************** * t h i r d * * Return the contents of the third list element. * * #define third(l) first_node (list_rest (list_rest (l))) **********************************************************************/ #define third(l) \ first_node (list_rest (list_rest (l))) /*---------------------------------------------------------------------- Public Function Prototypes ----------------------------------------------------------------------*/ int count(LIST var_list); LIST delete_d(LIST list, void *key, int_compare is_equal); LIST delete_d(LIST list, void *key, TessResultCallback2* is_equal); LIST destroy(LIST list); void destroy_nodes(LIST list, void_dest destructor); void insert(LIST list, void *node); int is_same_node(void *item1, void *item2); int is_same(void *item1, void *item2); LIST join(LIST list1, LIST list2); LIST last(LIST var_list); void *nth_cell(LIST var_list, int item_num); LIST pop(LIST list); LIST push(LIST list, void *element); LIST push_last(LIST list, void *item); LIST reverse(LIST list); LIST reverse_d(LIST list); LIST s_adjoin(LIST var_list, void *variable, int_compare compare); LIST search(LIST list, void *key, int_compare is_equal); LIST search(LIST list, void *key, TessResultCallback2*); /* #if defined(__STDC__) || defined(__cplusplus) # define _ARGS(s) s #else # define _ARGS(s) () #endif typedef void (*destructor) _ARGS((LIST l)); typedef LIST (*list_proc) _ARGS((LIST a)); int count _ARGS((LIST var_list)); LIST delete_d _ARGS((LIST list, LIST key, int_compare is_equal)); LIST destroy _ARGS((LIST list)); LIST destroy_nodes _ARGS((LIST list, void_dest destructor)); void insert _ARGS((LIST list, LIST node)); int is_same_node _ARGS((LIST s1, LIST s2)); int is_same _ARGS((LIST s1, LIST s2)); LIST join _ARGS((LIST list1, LIST list2)); LIST last _ARGS((LIST var_list)); LIST nth_cell _ARGS((LIST var_list, int item_num)); LIST pop _ARGS((LIST list)); LIST push _ARGS((LIST list, LIST element)); LIST push_last _ARGS((LIST list, LIST item)); LIST reverse _ARGS((LIST list)); LIST reverse_d _ARGS((LIST list)); LIST s_adjoin _ARGS((LIST var_list, LIST variable, int_compare compare)); LIST search _ARGS((LIST list, LIST key, int_compare is_equal)); #undef _ARGS */ #endif tesseract-3.04.01/cutil/structures.cpp000066400000000000000000000030771266071204500177350ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: structures.c (Formerly structures.c) * Description: Allocate all the different types of structures. * Author: Mark Seaman, OCR Technology * Created: Wed May 30 10:27:26 1990 * Modified: Mon Jul 15 10:39:18 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 "structures.h" #include /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ makestructure(new_cell, free_cell, list_rec); tesseract-3.04.01/cutil/structures.h000066400000000000000000000054371266071204500174040ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: structures.h (Formerly structures.h) * Description: Allocate all the different types of structures. * Author: Mark Seaman, OCR Technology * Created: Wed May 30 10:12:12 1990 * Modified: Tue May 21 11:07: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. * *********************************************************************************/ #ifndef STRUCTURES_H #define STRUCTURES_H /*---------------------------------------------------------------------- I n c l u d e s ----------------------------------------------------------------------*/ #include "oldlist.h" #include "freelist.h" #include "danerror.h" /*---------------------------------------------------------------------- M a c r o s ----------------------------------------------------------------------*/ /********************************************************************** * makestructure * * Allocate a chunk of memory for a particular data type. This macro * defines an allocation, deallocation, and status printing function * for each new data type. **********************************************************************/ #define makestructure(newfunc, old, type) \ type *newfunc() \ { \ return new type; \ } \ \ \ \ void old(type* deadelement) \ { \ delete deadelement; \ } \ /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ extern LIST new_cell(); extern void free_cell(LIST); #endif tesseract-3.04.01/dict/000077500000000000000000000000001266071204500146025ustar00rootroot00000000000000tesseract-3.04.01/dict/Makefile.am000066400000000000000000000014731266071204500166430ustar00rootroot00000000000000AM_CPPFLAGS += -I$(top_srcdir)/cutil -I$(top_srcdir)/ccutil \ -I$(top_srcdir)/ccstruct -I$(top_srcdir)/viewer if VISIBILITY AM_CPPFLAGS += -DTESS_EXPORTS \ -fvisibility=hidden -fvisibility-inlines-hidden endif noinst_HEADERS = \ dawg.h dawg_cache.h dict.h matchdefs.h \ stopper.h trie.h if !USING_MULTIPLELIBS noinst_LTLIBRARIES = libtesseract_dict.la else lib_LTLIBRARIES = libtesseract_dict.la libtesseract_dict_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) libtesseract_dict_la_LIBADD = \ ../ccutil/libtesseract_ccutil.la \ ../cutil/libtesseract_cutil.la \ ../ccstruct/libtesseract_ccstruct.la \ ../viewer/libtesseract_viewer.la endif libtesseract_dict_la_SOURCES = \ context.cpp \ dawg.cpp dawg_cache.cpp dict.cpp hyphen.cpp \ permdawg.cpp stopper.cpp trie.cpp tesseract-3.04.01/dict/Makefile.in000066400000000000000000000551621266071204500166600ustar00rootroot00000000000000# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @VISIBILITY_TRUE@am__append_1 = -DTESS_EXPORTS \ @VISIBILITY_TRUE@ -fvisibility=hidden -fvisibility-inlines-hidden subdir = dict DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/config/depcomp $(noinst_HEADERS) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config_auto.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) @USING_MULTIPLELIBS_TRUE@libtesseract_dict_la_DEPENDENCIES = \ @USING_MULTIPLELIBS_TRUE@ ../ccutil/libtesseract_ccutil.la \ @USING_MULTIPLELIBS_TRUE@ ../cutil/libtesseract_cutil.la \ @USING_MULTIPLELIBS_TRUE@ ../ccstruct/libtesseract_ccstruct.la \ @USING_MULTIPLELIBS_TRUE@ ../viewer/libtesseract_viewer.la am_libtesseract_dict_la_OBJECTS = context.lo dawg.lo dawg_cache.lo \ dict.lo hyphen.lo permdawg.lo stopper.lo trie.lo libtesseract_dict_la_OBJECTS = $(am_libtesseract_dict_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libtesseract_dict_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ $(AM_CXXFLAGS) $(CXXFLAGS) $(libtesseract_dict_la_LDFLAGS) \ $(LDFLAGS) -o $@ @USING_MULTIPLELIBS_FALSE@am_libtesseract_dict_la_rpath = @USING_MULTIPLELIBS_TRUE@am_libtesseract_dict_la_rpath = -rpath \ @USING_MULTIPLELIBS_TRUE@ $(libdir) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__depfiles_maybe = depfiles am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(libtesseract_dict_la_SOURCES) DIST_SOURCES = $(libtesseract_dict_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_CPPFLAGS = @AM_CPPFLAGS@ -I$(top_srcdir)/cutil \ -I$(top_srcdir)/ccutil -I$(top_srcdir)/ccstruct \ -I$(top_srcdir)/viewer $(am__append_1) AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AM_LDFLAGS = @AM_LDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FRAMEWORK_OPENCL = @FRAMEWORK_OPENCL@ GENERIC_API_VERSION = @GENERIC_API_VERSION@ GENERIC_LIBRARY_NAME = @GENERIC_LIBRARY_NAME@ GENERIC_LIBRARY_VERSION = @GENERIC_LIBRARY_VERSION@ GENERIC_MAJOR_VERSION = @GENERIC_MAJOR_VERSION@ GENERIC_RELEASE = @GENERIC_RELEASE@ GENERIC_VERSION = @GENERIC_VERSION@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBLEPT_HEADERSDIR = @LIBLEPT_HEADERSDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCL_CPPFLAGS = @OPENCL_CPPFLAGS@ OPENCL_LDFLAGS = @OPENCL_LDFLAGS@ OPENMP_CXXFLAGS = @OPENMP_CXXFLAGS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_DATE = @PACKAGE_DATE@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_YEAR = @PACKAGE_YEAR@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_HEADERS = \ dawg.h dawg_cache.h dict.h matchdefs.h \ stopper.h trie.h @USING_MULTIPLELIBS_FALSE@noinst_LTLIBRARIES = libtesseract_dict.la @USING_MULTIPLELIBS_TRUE@lib_LTLIBRARIES = libtesseract_dict.la @USING_MULTIPLELIBS_TRUE@libtesseract_dict_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) @USING_MULTIPLELIBS_TRUE@libtesseract_dict_la_LIBADD = \ @USING_MULTIPLELIBS_TRUE@ ../ccutil/libtesseract_ccutil.la \ @USING_MULTIPLELIBS_TRUE@ ../cutil/libtesseract_cutil.la \ @USING_MULTIPLELIBS_TRUE@ ../ccstruct/libtesseract_ccstruct.la \ @USING_MULTIPLELIBS_TRUE@ ../viewer/libtesseract_viewer.la libtesseract_dict_la_SOURCES = \ context.cpp \ dawg.cpp dawg_cache.cpp dict.cpp hyphen.cpp \ permdawg.cpp stopper.cpp trie.cpp all: all-am .SUFFIXES: .SUFFIXES: .cpp .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign dict/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign dict/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libtesseract_dict.la: $(libtesseract_dict_la_OBJECTS) $(libtesseract_dict_la_DEPENDENCIES) $(EXTRA_libtesseract_dict_la_DEPENDENCIES) $(AM_V_CXXLD)$(libtesseract_dict_la_LINK) $(am_libtesseract_dict_la_rpath) $(libtesseract_dict_la_OBJECTS) $(libtesseract_dict_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/context.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dawg.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dawg_cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hyphen.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/permdawg.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stopper.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trie.Plo@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ clean-noinstLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-libLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-libLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libLTLIBRARIES clean-libtool clean-noinstLTLIBRARIES \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-libLTLIBRARIES \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-libLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: tesseract-3.04.01/dict/context.cpp000066400000000000000000000061541266071204500170000ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: context.c (Formerly context.c) * Description: Context checking functions * Author: Mark Seaman, OCR Technology * Created: Thu Feb 15 11:18:24 1990 * Modified: Tue Jul 9 17:38:16 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 "dict.h" #include "tprintf.h" #include "unicharset.h" namespace tesseract { static const int kMinAbsoluteGarbageWordLength = 10; static const float kMinAbsoluteGarbageAlphanumFrac = 0.5f; const int case_state_table[6][4] = { { /* 0. Beginning of word */ /* P U L D */ /* -1. Error on case */ 0, 1, 5, 4 }, { /* 1. After initial capital */ 0, 3, 2, 4 }, { /* 2. After lower case */ 0, -1, 2, -1 }, { /* 3. After upper case */ 0, 3, -1, 4 }, { /* 4. After a digit */ 0, -1, -1, 4 }, { /* 5. After initial lower case */ 5, -1, 2, -1 }, }; int Dict::case_ok(const WERD_CHOICE &word, const UNICHARSET &unicharset) { int state = 0; int x; for (x = 0; x < word.length(); ++x) { UNICHAR_ID ch_id = word.unichar_id(x); if (unicharset.get_isupper(ch_id)) state = case_state_table[state][1]; else if (unicharset.get_islower(ch_id)) state = case_state_table[state][2]; else if (unicharset.get_isdigit(ch_id)) state = case_state_table[state][3]; else state = case_state_table[state][0]; if (state == -1) return false; } return state != 5; // single lower is bad } bool Dict::absolute_garbage(const WERD_CHOICE &word, const UNICHARSET &unicharset) { if (word.length() < kMinAbsoluteGarbageWordLength) return false; int num_alphanum = 0; for (int x = 0; x < word.length(); ++x) { num_alphanum += (unicharset.get_isalpha(word.unichar_id(x)) || unicharset.get_isdigit(word.unichar_id(x))); } return (static_cast(num_alphanum) / static_cast(word.length()) < kMinAbsoluteGarbageAlphanumFrac); } } // namespace tesseract tesseract-3.04.01/dict/dawg.cpp000066400000000000000000000337661266071204500162470ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: dawg.c (Formerly dawg.c) * Description: Use a Directed Accyclic Word Graph * Author: Mark Seaman, OCR Technology * Created: Fri Oct 16 14:37:00 1987 * Modified: Wed Jul 24 16:59:16 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 ----------------------------------------------------------------------*/ #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #pragma warning(disable:4800) // int/bool warnings #endif #include "dawg.h" #include "cutil.h" #include "dict.h" #include "emalloc.h" #include "freelist.h" #include "helpers.h" #include "strngs.h" #include "tesscallback.h" #include "tprintf.h" /*---------------------------------------------------------------------- F u n c t i o n s f o r D a w g ----------------------------------------------------------------------*/ namespace tesseract { 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++) { EDGE_REF edge = edge_char_of(node, word.unichar_id(i), false); if (edge == NO_EDGE) { return false; } 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; } } // 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, const UNICHARSET &unicharset, bool enable_wildcard) const { if (filename == NULL) return 0; FILE *word_file; char string [CHARS_PER_LINE]; int misses = 0; UNICHAR_ID wildcard = unicharset.unichar_to_id(kWildcard); word_file = open_file (filename, "r"); while (fgets (string, CHARS_PER_LINE, word_file) != NULL) { chomp_string(string); // remove newline WERD_CHOICE word(string, unicharset); if (word.length() > 0 && !word.contains_unichar_id(INVALID_UNICHAR_ID)) { if (!match_words(&word, 0, 0, enable_wildcard ? wildcard : INVALID_UNICHAR_ID)) { tprintf("Missing word: %s\n", string); ++misses; } } else { tprintf("Failed to create a valid word from %s\n", string); } } fclose (word_file); // Make sure the user sees this with fprintf instead of tprintf. if (debug_level_) tprintf("Number of lost words=%d\n", misses); return misses; } void Dawg::iterate_words(const UNICHARSET &unicharset, 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 { NodeChildVector 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)) { cb->Run(&next_word); } NODE_REF next = next_node(children[i].edge_ref); if (next != 0) { iterate_words_rec(next_word, next, cb); } } } bool Dawg::match_words(WERD_CHOICE *word, inT32 index, NODE_REF node, UNICHAR_ID wildcard) const { EDGE_REF edge; inT32 word_end; if (wildcard != INVALID_UNICHAR_ID && word->unichar_id(index) == wildcard) { bool any_matched = false; NodeChildVector 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)) any_matched = true; } word->set_unichar_id(wildcard, index); return any_matched; } else { word_end = index == word->length() - 1; edge = edge_char_of(node, word->unichar_id(index), word_end); if (edge != NO_EDGE) { // normal edge in DAWG node = next_node(edge); if (word_end) { if (debug_level_ > 1) word->print("match_words() found: "); return true; } else if (node != 0) { return match_words(word, index+1, node, wildcard); } } } return false; } void Dawg::init(DawgType type, const STRING &lang, PermuterType perm, int unicharset_size, int debug_level) { type_ = type; lang_ = lang; perm_ = perm; ASSERT_HOST(unicharset_size > 0); unicharset_size_ = unicharset_size; // Set bit masks. We will use the value unicharset_size_ as a null char, so // the actual number of unichars is unicharset_size_ + 1. flag_start_bit_ = ceil(log(unicharset_size_ + 1.0) / log(2.0)); next_node_start_bit_ = flag_start_bit_ + NUM_FLAG_BITS; letter_mask_ = ~(~0ull << flag_start_bit_); next_node_mask_ = ~0ull << (flag_start_bit_ + NUM_FLAG_BITS); flags_mask_ = ~(letter_mask_ | next_node_mask_); debug_level_ = debug_level; } /*---------------------------------------------------------------------- F u n c t i o n s f o r S q u i s h e d D a w g ----------------------------------------------------------------------*/ SquishedDawg::~SquishedDawg() { memfree(edges_); } EDGE_REF SquishedDawg::edge_char_of(NODE_REF node, UNICHAR_ID unichar_id, bool word_end) const { EDGE_REF edge = node; if (node == 0) { // binary search EDGE_REF start = 0; EDGE_REF end = num_forward_edges_in_node0 - 1; int compare; while (start <= end) { edge = (start + end) >> 1; // (start + end) / 2 compare = given_greater_than_edge_rec(NO_EDGE, word_end, unichar_id, edges_[edge]); if (compare == 0) { // given == vec[k] return edge; } else if (compare == 1) { // given > vec[k] start = edge + 1; } else { // given < vec[k] end = edge - 1; } } } else { // linear search if (edge != NO_EDGE && edge_occupied(edge)) { do { if ((unichar_id_from_edge_rec(edges_[edge]) == unichar_id) && (!word_end || end_of_word_from_edge_rec(edges_[edge]))) return (edge); } while (!last_edge(edge++)); } } return (NO_EDGE); // not found } inT32 SquishedDawg::num_forward_edges(NODE_REF node) const { EDGE_REF edge = node; inT32 num = 0; if (forward_edge (edge)) { do { num++; } while (!last_edge(edge++)); } return (num); } void SquishedDawg::print_node(NODE_REF node, int max_num_edges) const { if (node == NO_EDGE) return; // nothing to print EDGE_REF edge = node; const char *forward_string = "FORWARD"; const char *backward_string = " "; const char *last_string = "LAST"; const char *not_last_string = " "; const char *eow_string = "EOW"; const char *not_eow_string = " "; const char *direction; const char *is_last; const char *eow; UNICHAR_ID unichar_id; if (edge_occupied(edge)) { do { direction = forward_edge(edge) ? forward_string : backward_string; is_last = last_edge(edge) ? last_string : not_last_string; eow = end_of_word(edge) ? eow_string : not_eow_string; unichar_id = edge_letter(edge); tprintf(REFFORMAT " : next = " REFFORMAT ", unichar_id = %d, %s %s %s\n", edge, next_node(edge), unichar_id, direction, is_last, eow); if (edge - node > max_num_edges) return; } while (!last_edge(edge++)); if (edge < num_edges_ && edge_occupied(edge) && backward_edge(edge)) { do { direction = forward_edge(edge) ? forward_string : backward_string; is_last = last_edge(edge) ? last_string : not_last_string; eow = end_of_word(edge) ? eow_string : not_eow_string; unichar_id = edge_letter(edge); tprintf(REFFORMAT " : next = " REFFORMAT ", unichar_id = %d, %s %s %s\n", edge, next_node(edge), unichar_id, direction, is_last, eow); if (edge - node > MAX_NODE_EDGES_DISPLAY) return; } while (!last_edge(edge++)); } } else { tprintf(REFFORMAT " : no edges in this node\n", node); } tprintf("\n"); } void SquishedDawg::print_edge(EDGE_REF edge) const { if (edge == NO_EDGE) { tprintf("NO_EDGE\n"); } else { tprintf(REFFORMAT " : next = " REFFORMAT ", unichar_id = '%d', %s %s %s\n", edge, next_node(edge), edge_letter(edge), (forward_edge(edge) ? "FORWARD" : " "), (last_edge(edge) ? "LAST" : " "), (end_of_word(edge) ? "EOW" : "")); } } void SquishedDawg::read_squished_dawg(FILE *file, DawgType type, const STRING &lang, PermuterType perm, int debug_level) { if (debug_level) tprintf("Reading squished dawg\n"); // Read the magic number and if it does not match kDawgMagicNumber // set swap to true to indicate that we need to switch endianness. inT16 magic; fread(&magic, sizeof(inT16), 1, file); bool swap = (magic != kDawgMagicNumber); int unicharset_size; fread(&unicharset_size, sizeof(inT32), 1, file); fread(&num_edges_, sizeof(inT32), 1, file); if (swap) { ReverseN(&unicharset_size, sizeof(unicharset_size)); ReverseN(&num_edges_, sizeof(num_edges_)); } ASSERT_HOST(num_edges_ > 0); // DAWG should not be empty Dawg::init(type, lang, perm, unicharset_size, debug_level); edges_ = (EDGE_ARRAY) memalloc(sizeof(EDGE_RECORD) * num_edges_); fread(&edges_[0], sizeof(EDGE_RECORD), num_edges_, file); EDGE_REF edge; if (swap) { for (edge = 0; edge < num_edges_; ++edge) { ReverseN(&edges_[edge], sizeof(edges_[edge])); } } if (debug_level > 2) { tprintf("type: %d lang: %s perm: %d unicharset_size: %d num_edges: %d\n", type_, lang_.string(), perm_, unicharset_size_, num_edges_); for (edge = 0; edge < num_edges_; ++edge) print_edge(edge); } } NODE_MAP SquishedDawg::build_node_map(inT32 *num_nodes) const { EDGE_REF edge; NODE_MAP node_map; inT32 node_counter; inT32 num_edges; node_map = (NODE_MAP) malloc(sizeof(EDGE_REF) * num_edges_); for (edge = 0; edge < num_edges_; edge++) // init all slots node_map [edge] = -1; node_counter = num_forward_edges(0); *num_nodes = 0; for (edge = 0; edge < num_edges_; edge++) { // search all slots if (forward_edge(edge)) { (*num_nodes)++; // count nodes links node_map[edge] = (edge ? node_counter : 0); num_edges = num_forward_edges(edge); if (edge != 0) node_counter += num_edges; edge += num_edges; if (edge >= num_edges_) break; if (backward_edge(edge)) while (!last_edge(edge++)); edge--; } } return (node_map); } void SquishedDawg::write_squished_dawg(FILE *file) { EDGE_REF edge; inT32 num_edges; inT32 node_count = 0; NODE_MAP node_map; EDGE_REF old_index; EDGE_RECORD temp_record; if (debug_level_) tprintf("write_squished_dawg\n"); node_map = build_node_map(&node_count); // Write the magic number to help detecting a change in endianness. inT16 magic = kDawgMagicNumber; fwrite(&magic, sizeof(inT16), 1, file); fwrite(&unicharset_size_, sizeof(inT32), 1, file); // Count the number of edges in this Dawg. num_edges = 0; for (edge=0; edge < num_edges_; edge++) if (forward_edge(edge)) num_edges++; fwrite(&num_edges, sizeof(inT32), 1, file); // write edge count to file if (debug_level_) { tprintf("%d nodes in DAWG\n", node_count); tprintf("%d edges in DAWG\n", num_edges); } for (edge = 0; edge < num_edges_; edge++) { if (forward_edge(edge)) { // write forward edges do { old_index = next_node_from_edge_rec(edges_[edge]); set_next_node(edge, node_map[old_index]); temp_record = edges_[edge]; fwrite(&(temp_record), sizeof(EDGE_RECORD), 1, file); set_next_node(edge, old_index); } while (!last_edge(edge++)); if (edge >= num_edges_) break; if (backward_edge(edge)) // skip back links while (!last_edge(edge++)); edge--; } } free(node_map); } } // namespace tesseract tesseract-3.04.01/dict/dawg.h000066400000000000000000000526571266071204500157140ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: dawg.h (Formerly dawg.h) * Description: Definition of a class that represents Directed Accyclic Word * Graph (DAWG), functions to build and manipulate the DAWG. * Author: Mark Seaman, SW Productivity * Created: Fri Oct 16 14:37:00 1987 * Modified: Wed Jun 19 16:50:24 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 DICT_DAWG_H_ #define DICT_DAWG_H_ /*---------------------------------------------------------------------- I n c l u d e s ----------------------------------------------------------------------*/ #include "elst.h" #include "ratngs.h" #include "params.h" #include "tesscallback.h" #ifndef __GNUC__ #ifdef _WIN32 #define NO_EDGE (inT64) 0xffffffffffffffffi64 #endif /*_WIN32*/ #else #define NO_EDGE (inT64) 0xffffffffffffffffll #endif /*__GNUC__*/ /*---------------------------------------------------------------------- T y p e s ----------------------------------------------------------------------*/ class UNICHARSET; typedef uinT64 EDGE_RECORD; typedef EDGE_RECORD *EDGE_ARRAY; typedef inT64 EDGE_REF; typedef inT64 NODE_REF; typedef EDGE_REF *NODE_MAP; namespace tesseract { struct NodeChild { UNICHAR_ID unichar_id; EDGE_REF edge_ref; NodeChild(UNICHAR_ID id, EDGE_REF ref): unichar_id(id), edge_ref(ref) {} NodeChild(): unichar_id(INVALID_UNICHAR_ID), edge_ref(NO_EDGE) {} }; typedef GenericVector NodeChildVector; typedef GenericVector SuccessorList; typedef GenericVector SuccessorListsVector; enum DawgType { DAWG_TYPE_PUNCTUATION, DAWG_TYPE_WORD, DAWG_TYPE_NUMBER, DAWG_TYPE_PATTERN, DAWG_TYPE_COUNT // number of enum entries }; /*---------------------------------------------------------------------- C o n s t a n t s ----------------------------------------------------------------------*/ #define FORWARD_EDGE (inT32) 0 #define BACKWARD_EDGE (inT32) 1 #define MAX_NODE_EDGES_DISPLAY (inT64) 100 #define MARKER_FLAG (inT64) 1 #define DIRECTION_FLAG (inT64) 2 #define WERD_END_FLAG (inT64) 4 #define LETTER_START_BIT 0 #define NUM_FLAG_BITS 3 #define REFFORMAT "%lld" 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 { 1, 0, 0, 0 }, // for DAWG_TYPE_NUMBER { 0, 0, 0, 0 }, // for DAWG_TYPE_PATTERN }; static const char kWildcard[] = "*"; /*---------------------------------------------------------------------- C l a s s e s a n d S t r u c t s ----------------------------------------------------------------------*/ // /// Abstract class (an interface) that declares methods needed by the /// various tesseract classes to operate on SquishedDawg and Trie objects. /// /// This class initializes all the edge masks (since their usage by /// SquishedDawg and Trie is identical) and implements simple accessors /// for each of the fields encoded in an EDGE_RECORD. /// This class also implements word_in_dawg() and check_for_words() /// (since they use only the public methods of SquishedDawg and Trie /// classes that are inherited from the Dawg base class). // class Dawg { public: /// Magic number to determine endianness when reading the Dawg from file. static const inT16 kDawgMagicNumber = 42; /// A special unichar id that indicates that any appropriate pattern /// (e.g.dicitonary word, 0-9 digit, etc) can be inserted instead /// Used for expressing patterns in punctuation and number Dawgs. static const UNICHAR_ID kPatternUnicharID = 0; inline DawgType type() const { return type_; } inline const STRING &lang() const { return lang_; } inline PermuterType permuter() const { return perm_; } virtual ~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, TessCallback1 *cb) const; // Pure virtual function that should be implemented by the derived classes. /// Returns the edge that corresponds to the letter out of this node. virtual EDGE_REF edge_char_of(NODE_REF node, UNICHAR_ID unichar_id, bool word_end) const = 0; /// 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, bool word_end) const = 0; /// Returns the next node visited by following the edge /// indicated by the given EDGE_REF. virtual NODE_REF next_node(EDGE_REF edge_ref) const = 0; /// Returns true if the edge indicated by the given EDGE_REF /// marks the end of a word. virtual bool end_of_word(EDGE_REF edge_ref) const = 0; /// Returns UNICHAR_ID stored in the edge indicated by the given EDGE_REF. virtual UNICHAR_ID edge_letter(EDGE_REF edge_ref) const = 0; /// Prints the contents of the node indicated by the given NODE_REF. /// At most max_num_edges will be printed. virtual void print_node(NODE_REF node, int max_num_edges) const = 0; /// Fills vec with unichar ids that represent the character classes /// of the given unichar_id. virtual void unichar_id_to_patterns(UNICHAR_ID unichar_id, const UNICHARSET &unicharset, GenericVector *vec) const {}; /// Returns the given EDGE_REF if the EDGE_RECORD that it points to has /// a self loop and the given unichar_id matches the unichar_id stored in the /// EDGE_RECORD, returns NO_EDGE otherwise. virtual EDGE_REF pattern_loop_edge( EDGE_REF edge_ref, UNICHAR_ID unichar_id, bool word_end) const { return false; } protected: Dawg() {} /// Returns the next node visited by following this edge. inline NODE_REF next_node_from_edge_rec(const EDGE_RECORD &edge_rec) const { return ((edge_rec & next_node_mask_) >> next_node_start_bit_); } /// Returns the marker flag of this edge. inline bool marker_flag_from_edge_rec(const EDGE_RECORD &edge_rec) const { return (edge_rec & (MARKER_FLAG << flag_start_bit_)) != 0; } /// Returns the direction flag of this edge. inline int direction_from_edge_rec(const EDGE_RECORD &edge_rec) const { return ((edge_rec & (DIRECTION_FLAG << flag_start_bit_))) ? BACKWARD_EDGE : FORWARD_EDGE; } /// Returns true if this edge marks the end of a word. inline bool end_of_word_from_edge_rec(const EDGE_RECORD &edge_rec) const { return (edge_rec & (WERD_END_FLAG << flag_start_bit_)) != 0; } /// Returns UNICHAR_ID recorded in this edge. inline UNICHAR_ID unichar_id_from_edge_rec( const EDGE_RECORD &edge_rec) const { return ((edge_rec & letter_mask_) >> LETTER_START_BIT); } /// Sets the next node link for this edge in the Dawg. inline void set_next_node_in_edge_rec( EDGE_RECORD *edge_rec, EDGE_REF value) { *edge_rec &= (~next_node_mask_); *edge_rec |= ((value << next_node_start_bit_) & next_node_mask_); } /// Sets this edge record to be the last one in a sequence of edges. inline void set_marker_flag_in_edge_rec(EDGE_RECORD *edge_rec) { *edge_rec |= (MARKER_FLAG << flag_start_bit_); } /// Sequentially compares the given values of unichar ID, next node /// and word end marker with the values in the given EDGE_RECORD. /// Returns: 1 if at any step the given input value exceeds /// that of edge_rec (and all the values already /// checked are the same) /// 0 if edge_rec_match() returns true /// -1 otherwise inline int given_greater_than_edge_rec(NODE_REF next_node, bool word_end, UNICHAR_ID unichar_id, const EDGE_RECORD &edge_rec) const { UNICHAR_ID curr_unichar_id = unichar_id_from_edge_rec(edge_rec); NODE_REF curr_next_node = next_node_from_edge_rec(edge_rec); bool curr_word_end = end_of_word_from_edge_rec(edge_rec); if (edge_rec_match(next_node, word_end, unichar_id, curr_next_node, curr_word_end, curr_unichar_id)) return 0; if (unichar_id > curr_unichar_id) return 1; if (unichar_id == curr_unichar_id) { if (next_node > curr_next_node) return 1; if (next_node == curr_next_node) { if (word_end > curr_word_end) return 1; } } return -1; } /// Returns true if all the values are equal (any value matches /// next_node if next_node == NO_EDGE, any value matches word_end /// if word_end is false). inline bool edge_rec_match(NODE_REF next_node, bool word_end, UNICHAR_ID unichar_id, NODE_REF other_next_node, bool other_word_end, UNICHAR_ID other_unichar_id) const { return ((unichar_id == other_unichar_id) && (next_node == NO_EDGE || next_node == other_next_node) && (!word_end || (word_end == other_word_end))); } /// Sets type_, lang_, perm_, unicharset_size_. /// Initializes the values of various masks from unicharset_size_. void init(DawgType type, const STRING &lang, PermuterType perm, int unicharset_size, int debug_level); /// Matches all of the words that are represented by this string. /// If wilcard is set to something other than INVALID_UNICHAR_ID, /// the *'s in this string are interpreted as wildcards. /// WERD_CHOICE param is not passed by const so that wildcard searches /// can modify it and work without having to copy WERD_CHOICEs. bool match_words(WERD_CHOICE *word, inT32 index, NODE_REF node, UNICHAR_ID wildcard) const; // 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; // Member Variables. DawgType type_; STRING lang_; /// Permuter code that should be used if the word is found in this Dawg. PermuterType perm_; // Variables to construct various edge masks. Formerly: // #define NEXT_EDGE_MASK (inT64) 0xfffffff800000000i64 // #define FLAGS_MASK (inT64) 0x0000000700000000i64 // #define LETTER_MASK (inT64) 0x00000000ffffffffi64 int unicharset_size_; int flag_start_bit_; int next_node_start_bit_; uinT64 next_node_mask_; uinT64 flags_mask_; uinT64 letter_mask_; // Level of debug statements to print to stdout. int debug_level_; }; // // 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) // // 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; } 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 DawgPositionVector : public GenericVector { public: /// Overload destructor, since clear() does not delete data_[] any more. ~DawgPositionVector() { if (size_reserved_ > 0) { delete[] data_; size_used_ = 0; size_reserved_ = 0; } } /// Overload clear() in order to avoid allocating/deallocating memory /// when clearing the vector and re-inserting entries into it later. void clear() { size_used_ = 0; } /// 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 DawgPosition &new_pos, bool debug, const char *debug_msg) { for (int i = 0; i < size_used_; ++i) { if (data_[i] == new_pos) return false; } push_back(new_pos); if (debug) { 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; } }; // /// Concrete class that can operate on a compacted (squished) Dawg (read, /// search and write to file). This class is read-only in the sense that /// new words can not be added to an instance of SquishedDawg. /// The underlying representation of the nodes and edges in SquishedDawg /// is stored as a contiguous EDGE_ARRAY (read from file or given as an /// argument to the constructor). // class SquishedDawg : public Dawg { public: SquishedDawg(FILE *file, DawgType type, const STRING &lang, PermuterType perm, int debug_level) { read_squished_dawg(file, type, lang, perm, debug_level); num_forward_edges_in_node0 = num_forward_edges(0); } SquishedDawg(const char* filename, DawgType type, const STRING &lang, PermuterType perm, int debug_level) { FILE *file = fopen(filename, "rb"); if (file == NULL) { tprintf("Failed to open dawg file %s\n", filename); exit(1); } read_squished_dawg(file, type, lang, perm, debug_level); num_forward_edges_in_node0 = num_forward_edges(0); fclose(file); } SquishedDawg(EDGE_ARRAY edges, int num_edges, DawgType type, const STRING &lang, PermuterType perm, int unicharset_size, int debug_level) : edges_(edges), num_edges_(num_edges) { init(type, lang, perm, unicharset_size, debug_level); num_forward_edges_in_node0 = num_forward_edges(0); if (debug_level > 3) print_all("SquishedDawg:"); } ~SquishedDawg(); int NumEdges() { return num_edges_; } /// Returns the edge that corresponds to the letter out of this node. EDGE_REF edge_char_of(NODE_REF node, UNICHAR_ID unichar_id, bool word_end) const; /// 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, 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 function is called 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++)); } /// Returns the next node visited by following the edge /// indicated by the given EDGE_REF. NODE_REF next_node(EDGE_REF edge) const { return next_node_from_edge_rec((edges_[edge])); } /// Returns true if the edge indicated by the given EDGE_REF /// marks the end of a word. bool end_of_word(EDGE_REF edge_ref) const { return end_of_word_from_edge_rec((edges_[edge_ref])); } /// Returns UNICHAR_ID stored in the edge indicated by the given EDGE_REF. UNICHAR_ID edge_letter(EDGE_REF edge_ref) const { return unichar_id_from_edge_rec((edges_[edge_ref])); } /// Prints the contents of the node indicated by the given NODE_REF. /// At most max_num_edges will be printed. void print_node(NODE_REF node, int max_num_edges) const; /// Writes the squished/reduced Dawg to a file. void write_squished_dawg(FILE *file); /// Opens the file with the given filename and writes the /// squished/reduced Dawg to the file. void write_squished_dawg(const char *filename) { FILE *file = fopen(filename, "wb"); if (file == NULL) { tprintf("Error opening %s\n", filename); exit(1); } this->write_squished_dawg(file); fclose(file); } private: /// Sets the next node link for this edge. inline void set_next_node(EDGE_REF edge_ref, EDGE_REF value) { set_next_node_in_edge_rec(&(edges_[edge_ref]), value); } /// Sets the edge to be empty. inline void set_empty_edge(EDGE_REF edge_ref) { (edges_[edge_ref] = next_node_mask_); } /// Goes through all the edges and clears each one out. inline void clear_all_edges() { for (int edge = 0; edge < num_edges_; edge++) set_empty_edge(edge); } /// Clears the last flag of this edge. inline void clear_marker_flag(EDGE_REF edge_ref) { (edges_[edge_ref] &= ~(MARKER_FLAG << flag_start_bit_)); } /// Returns true if this edge is in the forward direction. inline bool forward_edge(EDGE_REF edge_ref) const { return (edge_occupied(edge_ref) && (FORWARD_EDGE == direction_from_edge_rec(edges_[edge_ref]))); } /// Returns true if this edge is in the backward direction. inline bool backward_edge(EDGE_REF edge_ref) const { return (edge_occupied(edge_ref) && (BACKWARD_EDGE == direction_from_edge_rec(edges_[edge_ref]))); } /// Returns true if the edge spot in this location is occupied. inline bool edge_occupied(EDGE_REF edge_ref) const { return (edges_[edge_ref] != next_node_mask_); } /// Returns true if this edge is the last edge in a sequence. inline bool last_edge(EDGE_REF edge_ref) const { return (edges_[edge_ref] & (MARKER_FLAG << flag_start_bit_)) != 0; } /// Counts and returns the number of forward edges in this node. inT32 num_forward_edges(NODE_REF node) const; /// Reads SquishedDawg from a file. void read_squished_dawg(FILE *file, DawgType type, const STRING &lang, PermuterType perm, int debug_level); /// Prints the contents of an edge indicated by the given EDGE_REF. void print_edge(EDGE_REF edge) const; /// Prints the contents of the SquishedDawg. void print_all(const char* msg) { tprintf("\n__________________________\n%s\n", msg); for (int i = 0; i < num_edges_; ++i) print_edge(i); tprintf("__________________________\n"); } /// Constructs a mapping from the memory node indices to disk node indices. NODE_MAP build_node_map(inT32 *num_nodes) const; // Member variables. EDGE_ARRAY edges_; int num_edges_; int num_forward_edges_in_node0; }; } // namespace tesseract #endif // DICT_DAWG_H_ tesseract-3.04.01/dict/dawg_cache.cpp000066400000000000000000000061671266071204500173650ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // 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 tesseract-3.04.01/dict/dawg_cache.h000066400000000000000000000032521266071204500170220ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // 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_ tesseract-3.04.01/dict/dict.cpp000066400000000000000000001007111266071204500162310ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: dict.cpp // Description: dict class. // Author: Samuel Charron // // (C) Copyright 2006, 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 "dict.h" #include "unicodes.h" #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #endif #include "tprintf.h" namespace tesseract { class Image; Dict::Dict(CCUtil* ccutil) : letter_is_okay_(&tesseract::Dict::def_letter_is_okay), probability_in_context_(&tesseract::Dict::def_probability_in_context), params_model_classify_(NULL), ccutil_(ccutil), STRING_MEMBER(user_words_file, "", "A filename of user-provided words.", getCCUtil()->params()), STRING_INIT_MEMBER(user_words_suffix, "", "A suffix of user-provided words located in tessdata.", getCCUtil()->params()), STRING_MEMBER(user_patterns_file, "", "A filename of user-provided patterns.", getCCUtil()->params()), STRING_INIT_MEMBER(user_patterns_suffix, "", "A suffix of user-provided patterns located in " "tessdata.", getCCUtil()->params()), BOOL_INIT_MEMBER(load_system_dawg, true, "Load system word dawg.", getCCUtil()->params()), BOOL_INIT_MEMBER(load_freq_dawg, true, "Load frequent word dawg.", getCCUtil()->params()), BOOL_INIT_MEMBER(load_unambig_dawg, true, "Load unambiguous word dawg.", getCCUtil()->params()), BOOL_INIT_MEMBER(load_punc_dawg, true, "Load dawg with punctuation" " patterns.", getCCUtil()->params()), BOOL_INIT_MEMBER(load_number_dawg, true, "Load dawg with number" " patterns.", getCCUtil()->params()), BOOL_INIT_MEMBER(load_bigram_dawg, true, "Load dawg with special word " "bigrams.", 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.", getCCUtil()->params()), double_MEMBER(xheight_penalty_inconsistent, 0.25, "Score penalty (0.1 = 10%) added if an xheight is " "inconsistent.", 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).", getCCUtil()->params()), double_MEMBER(segment_penalty_dict_case_ok, 1.1, "Score multiplier for word matches that have good case " "(lower is better).", getCCUtil()->params()), double_MEMBER(segment_penalty_dict_case_bad, 1.3125, "Default score multiplier for word matches, which may have " "case issues (lower is better).", getCCUtil()->params()), double_MEMBER(segment_penalty_ngram_best_choice, 1.24, "Multipler to for the best choice from the ngram model.", getCCUtil()->params()), double_MEMBER(segment_penalty_dict_nonword, 1.25, "Score multiplier for glyph fragment segmentations which " "do not match a dictionary word (lower is better).", getCCUtil()->params()), double_MEMBER(segment_penalty_garbage, 1.50, "Score multiplier for poorly cased strings that are not in" " the dictionary and generally look like garbage (lower is" " better).", getCCUtil()->params()), STRING_MEMBER(output_ambig_words_file, "", "Output file for ambiguities found in the dictionary", getCCUtil()->params()), INT_MEMBER(dawg_debug_level, 0, "Set to 1 for general debug info" ", to 2 for more details, to 3 to see all the debug messages", getCCUtil()->params()), INT_MEMBER(hyphen_debug_level, 0, "Debug level for hyphenated words.", getCCUtil()->params()), INT_MEMBER(max_viterbi_list_size, 10, "Maximum size of viterbi list.", getCCUtil()->params()), BOOL_MEMBER(use_only_first_uft8_step, false, "Use only the first UTF8 step of the given string" " when computing log probabilities.", getCCUtil()->params()), double_MEMBER(certainty_scale, 20.0, "Certainty scaling factor", getCCUtil()->params()), double_MEMBER(stopper_nondict_certainty_base, -2.50, "Certainty threshold for non-dict words", getCCUtil()->params()), double_MEMBER(stopper_phase2_certainty_rejection_offset, 1.0, "Reject certainty offset", getCCUtil()->params()), INT_MEMBER(stopper_smallword_size, 2, "Size of dict word to be treated as non-dict word", getCCUtil()->params()), double_MEMBER(stopper_certainty_per_char, -0.50, "Certainty to add" " for each dict char above small word size.", getCCUtil()->params()), double_MEMBER(stopper_allowable_character_badness, 3.0, "Max certaintly variation allowed in a word (in sigma)", getCCUtil()->params()), INT_MEMBER(stopper_debug_level, 0, "Stopper debug level", getCCUtil()->params()), BOOL_MEMBER(stopper_no_acceptable_choices, false, "Make AcceptableChoice() always return false. Useful" " when there is a need to explore all segmentations", getCCUtil()->params()), BOOL_MEMBER(save_raw_choices, false, "Deprecated- backward compatibility only", getCCUtil()->params()), INT_MEMBER(tessedit_truncate_wordchoice_log, 10, "Max words to keep in list", getCCUtil()->params()), STRING_MEMBER(word_to_debug, "", "Word for which stopper debug" " information should be printed to stdout", getCCUtil()->params()), STRING_MEMBER(word_to_debug_lengths, "", "Lengths of unichars in word_to_debug", getCCUtil()->params()), INT_MEMBER(fragments_debug, 0, "Debug character fragments", 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", getCCUtil()->params()), BOOL_MEMBER(save_doc_words, 0, "Save Document Words", getCCUtil()->params()), double_MEMBER(doc_dict_pending_threshold, 0.0, "Worst certainty for using pending dictionary", getCCUtil()->params()), double_MEMBER(doc_dict_certainty_threshold, -2.25, "Worst certainty for words that can be inserted into the" "document dictionary", 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.", getCCUtil()->params()) { dang_ambigs_table_ = NULL; replace_ambigs_table_ = NULL; reject_offset_ = 0.0; 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; unambig_dawg_ = NULL; wordseg_rating_adjust_factor_ = -1.0f; output_ambig_words_file_ = NULL; } Dict::~Dict() { if (hyphen_word_ != NULL) delete hyphen_word_; if (output_ambig_words_file_ != NULL) fclose(output_ambig_words_file_); } 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 = 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); 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 = getCCUtil()->tessdata_manager; const char *data_file_name = tessdata_manager.GetDataFileName().string(); // Load dawgs_. 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 || ((STRING &)user_words_file).length() > 0) { Trie *trie_ptr = new Trie(DAWG_TYPE_WORD, lang, USER_DAWG_PERM, getUnicharset().size(), dawg_debug_level); if (((STRING &)user_words_file).length() > 0) { name = user_words_file; } else { name = getCCUtil()->language_data_path_prefix; name += user_words_suffix; } 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()); delete trie_ptr; } else { dawgs_ += trie_ptr; } } if (((STRING &)user_patterns_suffix).length() > 0 || ((STRING &)user_patterns_file).length() > 0) { Trie *trie_ptr = new Trie(DAWG_TYPE_PATTERN, lang, USER_PATTERN_PERM, getUnicharset().size(), dawg_debug_level); trie_ptr->initialize_patterns(&(getUnicharset())); if (((STRING &)user_patterns_file).length() > 0) { name = user_patterns_file; } else { name = getCCUtil()->language_data_path_prefix; name += user_patterns_suffix; } if (!trie_ptr->read_pattern_list(name.string(), getUnicharset())) { tprintf("Error: failed to load %s\n", name.string()); delete trie_ptr; } else { dawgs_ += trie_ptr; } } document_words_ = new Trie(DAWG_TYPE_WORD, lang, DOC_DAWG_PERM, getUnicharset().size(), dawg_debug_level); dawgs_ += document_words_; // This dawg is temporary and should not be searched by letter_is_ok. pending_words_ = new Trie(DAWG_TYPE_WORD, lang, NO_PERM, getUnicharset().size(), dawg_debug_level); // 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. successors_.reserve(dawgs_.length()); for (int i = 0; i < dawgs_.length(); ++i) { const Dawg *dawg = dawgs_[i]; SuccessorList *lst = new SuccessorList(); for (int j = 0; j < dawgs_.length(); ++j) { const Dawg *other = dawgs_[j]; if (dawg != NULL && other != NULL && (dawg->lang() == other->lang()) && kDawgSuccessors[dawg->type()][other->type()]) *lst += j; } successors_ += lst; } } void Dict::End() { if (dawgs_.length() == 0) return; // Not safe to call twice. 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(); successors_.clear(); document_words_ = NULL; if (pending_words_ != NULL) { delete pending_words_; pending_words_ = NULL; } } // 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. int Dict::def_letter_is_okay(void* void_dawg_args, UNICHAR_ID unichar_id, bool word_end) const { DawgArgs *dawg_args = reinterpret_cast(void_dawg_args); if (dawg_debug_level >= 3) { tprintf("def_letter_is_okay: current unichar=%s word_end=%d" " num active dawgs=%d\n", getUnicharset().debug_str(unichar_id).string(), word_end, dawg_args->active_dawgs->length()); } // Do not accept words that contain kPatternUnicharID. // (otherwise pattern dawgs would not function correctly). // Do not accept words containing INVALID_UNICHAR_IDs. if (unichar_id == Dawg::kPatternUnicharID || unichar_id == INVALID_UNICHAR_ID) { dawg_args->permuter = NO_PERM; return NO_PERM; } // Initialization. PermuterType curr_perm = NO_PERM; dawg_args->updated_dawgs->clear(); // 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 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, 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 DawgPosition. continue; } // 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", pos.dawg_index, node, edge); } if (edge != NO_EDGE) { // the unichar was found in the current dawg if (dawg_debug_level >=3) { tprintf("Letter found in dawg %d\n", pos.dawg_index); } if (word_end && punc_dawg && !punc_dawg->end_of_word(pos.punc_ref)) { if (dawg_debug_level >= 3) { tprintf("Punctuation constraint not satisfied at end of word.\n"); } continue; } 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 // allows preserving information on which dawg the "core" word came from. // Keep the old value of dawg_args->permuter if it is COMPOUND_PERM. if (dawg_args->permuter == NO_PERM || curr_perm == NO_PERM || (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 DawgPosition &pos, UNICHAR_ID unichar_id, bool word_end, DawgPositionVector *updated_dawgs, PermuterType *curr_perm) const { 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; unichar_id_patterns.push_back(unichar_id); dawg->unichar_id_to_patterns(unichar_id, getUnicharset(), &unichar_id_patterns); for (int i = 0; i < unichar_id_patterns.size(); ++i) { // 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(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); } 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: "); } } } // 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(DawgPositionVector *active_dawgs, bool ambigs_mode) const { int i; 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].dawg_ref); } } } else { default_dawgs(active_dawgs, ambigs_mode); } } 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); } } } } } void Dict::add_document_word(const WERD_CHOICE &best_choice) { // Do not add hyphenated word parts to the document dawg. // hyphen_word_ will be non-NULL after the set_hyphen_word() is // called when the first part of the hyphenated word is // discovered and while the second part of the word is recognized. // hyphen_word_ is cleared in cc_recg() before the next word on // the line is recognized. if (hyphen_word_) return; char filename[CHARS_PER_LINE]; FILE *doc_word_file; int stringlen = best_choice.length(); if (valid_word(best_choice) || stringlen < 2) return; // Discard words that contain >= kDocDictMaxRepChars repeating unichars. if (best_choice.length() >= kDocDictMaxRepChars) { int num_rep_chars = 1; UNICHAR_ID uch_id = best_choice.unichar_id(0); for (int i = 1; i < best_choice.length(); ++i) { if (best_choice.unichar_id(i) != uch_id) { num_rep_chars = 1; uch_id = best_choice.unichar_id(i); } else { ++num_rep_chars; if (num_rep_chars == kDocDictMaxRepChars) return; } } } if (best_choice.certainty() < doc_dict_certainty_threshold || stringlen == 2) { if (best_choice.certainty() < doc_dict_pending_threshold) return; if (!pending_words_->word_in_dawg(best_choice)) { if (stringlen > 2 || (stringlen == 2 && getUnicharset().get_isupper(best_choice.unichar_id(0)) && getUnicharset().get_isupper(best_choice.unichar_id(1)))) { pending_words_->add_word_to_dawg(best_choice); } return; } } if (save_doc_words) { strcpy(filename, getCCUtil()->imagefile.string()); strcat(filename, ".doc"); doc_word_file = open_file (filename, "a"); fprintf(doc_word_file, "%s\n", best_choice.debug_string().string()); fclose(doc_word_file); } document_words_->add_word_to_dawg(best_choice); } void Dict::adjust_word(WERD_CHOICE *word, bool nonword, XHeightConsistencyEnum xheight_consistency, float additional_adjust, bool modify_rating, bool debug) { 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%s", nonword ? "Non-" : "", word->unichar_string().string(), word->rating(), xheight_triggered); } if (nonword) { // non-dictionary word if (case_is_ok && punc_is_ok) { adjust_factor += segment_penalty_dict_nonword; new_rating *= adjust_factor; if (debug) tprintf(", W"); } else { adjust_factor += segment_penalty_garbage; new_rating *= adjust_factor; if (debug) { if (!case_is_ok) tprintf(", C"); if (!punc_is_ok) tprintf(", P"); } } } else { // dictionary word if (case_is_ok) { if (!is_han && freq_dawg_ != NULL && freq_dawg_->word_in_dawg(*word)) { word->set_permuter(FREQ_DAWG_PERM); adjust_factor += segment_penalty_dict_frequent_word; new_rating *= adjust_factor; if (debug) tprintf(", F"); } else { adjust_factor += segment_penalty_dict_case_ok; new_rating *= adjust_factor; if (debug) tprintf(", "); } } else { adjust_factor += segment_penalty_dict_case_bad; new_rating *= adjust_factor; if (debug) tprintf(", C"); } } new_rating -= kRatingPad; if (modify_rating) word->set_rating(new_rating); if (debug) tprintf(" %4.2f --> %4.2f\n", adjust_factor, new_rating); word->set_adjust_factor(adjust_factor); } int Dict::valid_word(const WERD_CHOICE &word, bool numbers_ok) const { const WERD_CHOICE *word_ptr = &word; WERD_CHOICE temp_word(word.unicharset()); if (hyphenated() && hyphen_word_->unicharset() == word.unicharset()) { copy_hyphen_info(&temp_word); temp_word += word; word_ptr = &temp_word; } if (word_ptr->length() == 0) return NO_PERM; // Allocate vectors for holding current and updated // 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_dawgs == &(active_dawgs[1])) { dawg_args.updated_dawgs = &(active_dawgs[0]); ++(dawg_args.active_dawgs); } else { ++(dawg_args.updated_dawgs); dawg_args.active_dawgs = &(active_dawgs[0]); } } delete[] active_dawgs; return valid_word_permuter(dawg_args.permuter, numbers_ok) ? dawg_args.permuter : NO_PERM; } bool Dict::valid_bigram(const WERD_CHOICE &word1, const WERD_CHOICE &word2) const { if (bigram_dawg_ == NULL) return false; // Extract the core word from the middle of each word with any digits // replaced with question marks. int w1start, w1end, w2start, w2end; word1.punct_stripped(&w1start, &w1end); word2.punct_stripped(&w2start, &w2end); // We don't want to penalize a single guillemet, hyphen, etc. // But our bigram list doesn't have any information about punctuation. if (w1start >= w1end) return word1.length() < 3; if (w2start >= w2end) return word2.length() < 3; const UNICHARSET& uchset = getUnicharset(); GenericVector bigram_string; bigram_string.reserve(w1end + w2end + 1); for (int i = w1start; i < w1end; i++) { 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++) { 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); } return bigram_dawg_->word_in_dawg(normalized_word); } bool Dict::valid_punctuation(const WERD_CHOICE &word) { if (word.length() == 0) return NO_PERM; int i; WERD_CHOICE new_word(word.unicharset()); int last_index = word.length() - 1; int new_len = 0; for (i = 0; i <= last_index; ++i) { UNICHAR_ID unichar_id = (word.unichar_id(i)); if (getUnicharset().get_ispunctuation(unichar_id)) { new_word.append_unichar_id(unichar_id, 1, 0.0, 0.0); } else if (!getUnicharset().get_isalpha(unichar_id) && !getUnicharset().get_isdigit(unichar_id)) { return false; // neither punc, nor alpha, nor digit } else if ((new_len = new_word.length()) == 0 || new_word.unichar_id(new_len-1) != Dawg::kPatternUnicharID) { new_word.append_unichar_id(Dawg::kPatternUnicharID, 1, 0.0, 0.0); } } for (i = 0; i < dawgs_.size(); ++i) { if (dawgs_[i] != NULL && dawgs_[i]->type() == DAWG_TYPE_PUNCTUATION && dawgs_[i]->word_in_dawg(new_word)) return true; } return false; } } // namespace tesseract tesseract-3.04.01/dict/dict.h000066400000000000000000000707671266071204500157170ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////// // File: dict.h // Description: dict class. // Author: Samuel Charron // // (C) Copyright 2006, 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_DICT_H_ #define TESSERACT_DICT_DICT_H_ #include "ambigs.h" #include "dawg.h" #include "dawg_cache.h" #include "host.h" #include "oldlist.h" #include "ratngs.h" #include "stopper.h" #include "trie.h" #include "unicharset.h" #include "params_training_featdef.h" class MATRIX; class WERD_RES; #define MAX_WERD_LENGTH (inT64) 128 #define NO_RATING -1 /** Struct used to hold temporary information about fragments. */ struct CHAR_FRAGMENT_INFO { UNICHAR_ID unichar_id; const CHAR_FRAGMENT *fragment; int num_fragments; float rating; float certainty; }; namespace tesseract { typedef GenericVector DawgVector; // // Constants // 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 float kSimCertaintyScale = -10.0; // similarity matcher scaling 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(DawgPositionVector *d, DawgPositionVector *up, PermuterType p) : active_dawgs(d), updated_dawgs(up), permuter(p) {} DawgPositionVector *active_dawgs; DawgPositionVector *updated_dawgs; PermuterType permuter; }; class Dict { public: Dict(CCUtil* image_ptr); ~Dict(); const CCUtil* getCCUtil() const { return ccutil_; } CCUtil* getCCUtil() { return ccutil_; } const UNICHARSET& getUnicharset() const { return getCCUtil()->unicharset; } UNICHARSET& getUnicharset() { return getCCUtil()->unicharset; } const UnicharAmbigs &getUnicharAmbigs() const { return getCCUtil()->unichar_ambigs; } // Returns true if unichar_id is a word compounding character like - or /. inline bool compound_marker(UNICHAR_ID unichar_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_; } /// Size of the base word (the part on the line before) of a hyphenated word. inline int hyphen_base_size() const { return this->hyphenated() ? hyphen_word_->length() : 0; } /// If this word is hyphenated copy the base word (the part on /// the line before) of a hyphenated word into the given word. /// This function assumes that word is not NULL. inline void copy_hyphen_info(WERD_CHOICE *word) const { if (this->hyphenated()) { *word = *hyphen_word_; if (hyphen_debug_level) word->print("copy_hyphen_info: "); } } /// Check whether the word has a hyphen at the end. inline bool has_hyphen_end(UNICHAR_ID unichar_id, bool first_pos) const { 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 { int word_index = word.length() - 1; return has_hyphen_end(word.unichar_id(word_index), word_index == 0); } /// 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_, update last_word_on_line_. void reset_hyphen_vars(bool last_word_on_line); /// Update hyphen_word_, and copy the given DawgPositionVectors into /// hyphen_active_dawgs_ . void set_hyphen_word(const WERD_CHOICE &word, 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; } } /// 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(DawgPositionVector *active_dawgs, bool ambigs_mode) const; // 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); /// 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. 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, bool word_ending, WERD_CHOICE *word, float certainties[], float *limit, WERD_CHOICE *best_choice, int *attempts_left, void *void_more_args); /// 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, 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); void 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); 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, 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, MATRIX *ratings); /// Returns the length of the shortest alpha run in WordChoice. int LengthOfShortestAlphaRun(const WERD_CHOICE &WordChoice); /// 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 /// in which one character is much worse than the other characters in the /// 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 WERD_CHOICE& word); /// Returns true if the given best_choice is good enough to stop. 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(WERD_RES* word); void EndDangerousAmbigs(); /// Prints the current choices for this word to stdout. void DebugWordChoices(); /// Sets up stopper variables in preparation for the first pass. void SettupStopperPass1(); /// Sets up stopper variables in preparation for the second pass. void SettupStopperPass2(); /* context.cpp *************************************************************/ /// Check a string to see if it matches a set of lexical rules. int case_ok(const WERD_CHOICE &word, const UNICHARSET &unicharset); /// Returns true if the word looks like an absolute garbage /// (e.g. image mistakenly recognized as text). bool absolute_garbage(const WERD_CHOICE &word, const UNICHARSET &unicharset); /* dict.cpp ****************************************************************/ /// Initialize Dict class - load dawgs from [lang].traineddata and /// user-specified wordlist and parttern list. static DawgCache *GlobalDawgCache(); void Load(DawgCache *dawg_cache); void End(); // Resets the document dictionary analogous to ResetAdaptiveClassifier. void ResetDocumentDictionary() { if (pending_words_ != NULL) pending_words_->clear(); if (document_words_ != NULL) document_words_->clear(); } /** * 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 * is allowed according to at least one of the dawgs in dawgs_, * otherwise returns NO_PERM. * * The state is described by void_dawg_args, which are interpreted as * 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. 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 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_dawgs vector with the * entries for dawgs that contain the word up to the letter at word_index. * */ // int def_letter_is_okay(void* void_dawg_args, UNICHAR_ID unichar_id, bool word_end) const; int (Dict::*letter_is_okay_)(void* void_dawg_args, UNICHAR_ID unichar_id, bool word_end) const; /// Calls letter_is_okay_ member function. int LetterIsOkay(void* void_dawg_args, UNICHAR_ID unichar_id, bool word_end) const { return (this->*letter_is_okay_)(void_dawg_args, unichar_id, word_end); } /// Probability in context function used by the ngram permuter. double (Dict::*probability_in_context_)(const char* lang, const char* context, int context_bytes, const char* character, int character_bytes); /// Calls probability_in_context_ member function. double ProbabilityInContext(const char* context, int context_bytes, const char* character, int character_bytes) { return (this->*probability_in_context_)( getCCUtil()->lang.string(), context, context_bytes, character, character_bytes); } /// Default (no-op) implementation of probability in context function. double def_probability_in_context( const char* lang, const char* context, int context_bytes, const char* character, int character_bytes) { (void) context; (void) context_bytes; (void) character; (void) character_bytes; return 0.0; } double ngram_probability_in_context(const char* lang, const char* context, int context_bytes, 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_)( getCCUtil()->lang.string(), path); } inline void SetWildcardID(UNICHAR_ID id) { wildcard_unichar_id_ = id; } inline UNICHAR_ID WildcardID() const { return wildcard_unichar_id_; } /// Return the number of dawgs in the dawgs_ vector. inline int NumDawgs() const { return dawgs_.size(); } /// Return i-th dawg pointer recorded in the dawgs_ vector. inline const Dawg *GetDawg(int index) const { return dawgs_[index]; } /// Return the points to the punctuation dawg. inline const Dawg *GetPuncDawg() const { return punc_dawg_; } /// Return the points to the unambiguous words dawg. inline const Dawg *GetUnambigDawg() const { return unambig_dawg_; } /// 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 NODE_REF node = dawg->next_node(edge_ref); if (node == 0) node = NO_EDGE; // end of word return node; } // 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; } } /// For each of the character classes of the given unichar_id (and the /// unichar_id itself) finds the corresponding outgoing node or self-loop /// 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 DawgPosition &info, UNICHAR_ID unichar_id, bool word_end, 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). /// 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 || 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 { return valid_word(word, false); // return NO_PERM for words with digits } int valid_word_or_number(const WERD_CHOICE &word) const { return valid_word(word, true); // return NUMBER_PERM for valid numbers } /// This function is used by api/tesseract_cube_combiner.cpp int valid_word(const char *string) const { WERD_CHOICE word(string, getUnicharset()); return valid_word(word); } // Do the two WERD_CHOICEs form a meaningful bigram? bool valid_bigram(const WERD_CHOICE &word1, const WERD_CHOICE &word2) const; /// Returns true if the word contains a valid punctuation pattern. /// Note: Since the domains of punctuation symbols and symblos /// used in numbers are not disjoint, a valid number might contain /// an invalid punctuation pattern (e.g. .99). bool valid_punctuation(const WERD_CHOICE &word); /// Returns true if a good answer is found for the unknown blob rating. 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); /// Adjusts the rating of the given word. 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; } private: /** Private member variables. */ CCUtil* ccutil_; /** * Table that stores ambiguities computed during training * (loaded when NoDangerousAmbigs() is called for the first time). * Each entry i in the table stores a set of amibiguities whose * wrong ngram starts with unichar id i. */ UnicharAmbigs *dang_ambigs_table_; /** Same as above, but for ambiguities with replace flag set. */ UnicharAmbigs *replace_ambigs_table_; /** Additional certainty padding allowed before a word is rejected. */ FLOAT32 reject_offset_; // 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. WERD_CHOICE *hyphen_word_; 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_; Trie *pending_words_; // bigram_dawg_ points to a dawg of two-word bigrams which always supercede if // any of them are present on the best choices list for a word pair. // the bigrams are stored as space-separated words where: // (1) leading and trailing punctuation has been removed from each word and // (2) any digits have been replaced with '?' marks. Dawg *bigram_dawg_; /// The following pointers are only cached for convenience. /// The dawgs will be deleted when dawgs_ vector is destroyed. // TODO(daria): need to support multiple languages in the future, // so maybe will need to maintain a list of dawgs of each kind. Dawg *freq_dawg_; Dawg *unambig_dawg_; Dawg *punc_dawg_; Trie *document_words_; /// Current segmentation cost adjust factor for word rating. /// See comments in incorporate_segcost. float wordseg_rating_adjust_factor_; // File for recording ambiguities discovered during dictionary search. FILE *output_ambig_words_file_; public: /// Variable members. /// These have to be declared and initialized after image_ptr_, which contains /// the pointer to the params vector - the member of its base CCUtil class. STRING_VAR_H(user_words_file, "", "A filename of user-provided words."); STRING_VAR_H(user_words_suffix, "", "A suffix of user-provided words located in tessdata."); STRING_VAR_H(user_patterns_file, "", "A filename of user-provided patterns."); STRING_VAR_H(user_patterns_suffix, "", "A suffix of user-provided patterns located in tessdata."); BOOL_VAR_H(load_system_dawg, true, "Load system word dawg."); BOOL_VAR_H(load_freq_dawg, true, "Load frequent word dawg."); BOOL_VAR_H(load_unambig_dawg, true, "Load unambiguous word dawg."); 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_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)."); double_VAR_H(segment_penalty_dict_case_ok, 1.1, "Score multiplier for word matches that have good case " "(lower is better)."); double_VAR_H(segment_penalty_dict_case_bad, 1.3125, "Default score multiplier for word matches, which may have " "case issues (lower is better)."); // TODO(daria): remove this param when ngram permuter is deprecated. double_VAR_H(segment_penalty_ngram_best_choice, 1.24, "Multipler to for the best choice from the ngram model."); double_VAR_H(segment_penalty_dict_nonword, 1.25, "Score multiplier for glyph fragment segmentations which " "do not match a dictionary word (lower is better)."); double_VAR_H(segment_penalty_garbage, 1.50, "Score multiplier for poorly cased strings that are not in" " the dictionary and generally look like garbage (lower is" " better)."); STRING_VAR_H(output_ambig_words_file, "", "Output file for ambiguities found in the dictionary"); INT_VAR_H(dawg_debug_level, 0, "Set to 1 for general debug info" ", to 2 for more details, to 3 to see all the debug messages"); INT_VAR_H(hyphen_debug_level, 0, "Debug level for hyphenated words."); INT_VAR_H(max_viterbi_list_size, 10, "Maximum size of viterbi list."); BOOL_VAR_H(use_only_first_uft8_step, false, "Use only the first UTF8 step of the given string" " when computing log probabilities."); double_VAR_H(certainty_scale, 20.0, "Certainty scaling factor"); double_VAR_H(stopper_nondict_certainty_base, -2.50, "Certainty threshold for non-dict words"); double_VAR_H(stopper_phase2_certainty_rejection_offset, 1.0, "Reject certainty offset"); INT_VAR_H(stopper_smallword_size, 2, "Size of dict word to be treated as non-dict word"); double_VAR_H(stopper_certainty_per_char, -0.50, "Certainty to add for each dict char above small word size."); double_VAR_H(stopper_allowable_character_badness, 3.0, "Max certaintly variation allowed in a word (in sigma)"); INT_VAR_H(stopper_debug_level, 0, "Stopper debug level"); BOOL_VAR_H(stopper_no_acceptable_choices, false, "Make AcceptableChoice() always return false. Useful" " when there is a need to explore all segmentations"); BOOL_VAR_H(save_raw_choices, false, "Deprecated- backward compatibility only"); 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"); 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"); BOOL_VAR_H(save_doc_words, 0, "Save Document Words"); 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"); 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."); }; } // namespace tesseract #endif // THIRD_PARTY_TESSERACT_DICT_DICT_H_ tesseract-3.04.01/dict/hyphen.cpp000066400000000000000000000050311266071204500166000ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * File: hyphen.c (Formerly hyphen.c) * Description: Functions for maintaining information about hyphenated words. * Author: Mark Seaman, OCR Technology * Created: Fri Oct 16 14:37:00 1987 * Modified: Thu Mar 14 11:09:43 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 "dict.h" namespace tesseract { // 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_. void Dict::reset_hyphen_vars(bool last_word_on_line) { if (!(last_word_on_line_ == true && last_word_on_line == false)) { if (hyphen_word_ != NULL) { delete hyphen_word_; hyphen_word_ = NULL; hyphen_active_dawgs_.clear(); } } if (hyphen_debug_level) { tprintf("reset_hyphen_vars: last_word_on_line %d -> %d\n", last_word_on_line_, last_word_on_line); } last_word_on_line_ = last_word_on_line; } // Update hyphen_word_, and copy the given DawgPositionVectors into // hyphen_active_dawgs_. void Dict::set_hyphen_word(const WERD_CHOICE &word, const DawgPositionVector &active_dawgs) { if (hyphen_word_ == NULL) { hyphen_word_ = new WERD_CHOICE(word.unicharset()); hyphen_word_->make_bad(); } if (hyphen_word_->rating() > word.rating()) { *hyphen_word_ = word; // Remove the last unichar id as it is a hyphen, and remove // any unichar_string/lengths that are present. hyphen_word_->remove_last_unichar_id(); hyphen_active_dawgs_ = active_dawgs; } if (hyphen_debug_level) { hyphen_word_->print("set_hyphen_word: "); } } } // namespace tesseract tesseract-3.04.01/dict/matchdefs.h000066400000000000000000000100451266071204500167110ustar00rootroot00000000000000/****************************************************************************** ** Filename: matchdefs.h ** Purpose: Generic interface definitions for feature matchers. ** Author: Dan Johnson ** History: Fri Jan 19 09:21:25 1990, 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 MATCHDEFS_H #define MATCHDEFS_H /**---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------**/ #include "host.h" #include #include "unichar.h" /* define the maximum number of classes defined for any matcher and the maximum class id for any matcher. This must be changed if more different classes need to be classified */ #define MAX_NUM_CLASSES MAX_INT16 #define MAX_CLASS_ID (MAX_NUM_CLASSES - 1) /** a CLASS_ID is the ascii character to be associated with a class */ typedef UNICHAR_ID CLASS_ID; #define NO_CLASS (0) /** a PROTO_ID is the index of a prototype within it's class. Valid proto id's are 0 to N-1 where N is the number of prototypes that make up the class. */ typedef inT16 PROTO_ID; #define NO_PROTO (-1) /** FEATURE_ID is the index of a feature within a character description The feature id ranges from 0 to N-1 where N is the number of features in a character description. */ typedef uinT8 FEATURE_ID; #define NO_FEATURE 255 #define NOISE_FEATURE 254 #define MISSING_PROTO 254 #define MAX_NUM_FEAT 40 #define MAX_FEATURE_ID 250 /** a RATING is the match rating returned by a classifier. Higher is better. */ typedef FLOAT32 RATING; /** a CERTAINTY is an indication of the degree of confidence of the classifier. Higher is better. 0 means the match is as good as the mean of the matches seen in training. -1 means the match was one standard deviation worse than the training matches, etc. */ typedef FLOAT32 CERTAINTY; /** define a data structure to hold a single match result */ typedef struct { CLASS_ID Class; RATING Rating; CERTAINTY Certainty; } MATCH_RESULT; /** define a data structure for holding an array of match results */ typedef MATCH_RESULT SORTED_CLASSES[MAX_CLASS_ID + 1]; /*---------------------------------------------------------------------------- Public Function Prototypes ----------------------------------------------------------------------------*/ /** all feature matchers that are to be used with the high level classifier must support the following interface. The names will, of course, be unique for each different matcher. Note also that FEATURE_STRUCT is a data structure that is defined specifically for each feature extractor/matcher pair. */ /* misc test functions for proto id's and feature id's */ #define IsValidFeature(Fid) ((Fid) < MAX_FEATURE_ID) #define IsValidProto(Pid) ((Pid) >= 0) #if defined(__STDC__) || defined(__cplusplus) # define _ARGS(s) s #else # define _ARGS(s) () #endif /* matchdefs.c */ int CompareMatchResults _ARGS ((MATCH_RESULT * Result1, MATCH_RESULT * Result2)); void PrintMatchResult _ARGS ((FILE * File, MATCH_RESULT * MatchResult)); void PrintMatchResults _ARGS ((FILE * File, int N, MATCH_RESULT MatchResults[])); #undef _ARGS /*---------------------------------------------------------------------------- Global Data Definitions and Declarations ----------------------------------------------------------------------------*/ #endif tesseract-3.04.01/dict/permdawg.cpp000066400000000000000000000365611266071204500171270ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: permdawg.c (Formerly permdawg.c) * Description: Scale word choices by a dictionary * Author: Mark Seaman, OCR Technology * Created: Fri Oct 16 14:37:00 1987 * Modified: Tue Jul 9 15:43:18 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 "cutil.h" #include "dawg.h" #include "freelist.h" #include "globals.h" #include "ndminx.h" #include "stopper.h" #include "tprintf.h" #include "params.h" #include #include "dict.h" /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ namespace tesseract { /** * @name go_deeper_dawg_fxn * * If the choice being composed so far could be a dictionary word * keep exploring choices. */ void Dict::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, 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 == char_choices.size()-1); int word_index = word->length() - 1; if (best_choice->rating() < *limit) return; // Look up char in DAWG // 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()); } 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"); } } // 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); } } // 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()); } 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) { 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_, "%s", word_str.string()); } STRING word_str; word->string_and_lengths(&word_str, NULL); word_str += " "; fprintf(output_ambig_words_file_, "%s", word_str.string()); } 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 * * Recursively explore all the possible character combinations in * the given char_choices. Use go_deeper_dawg_fxn() to search 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 *Dict::dawg_permute_and_select( 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 || 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); float certainties[MAX_WERD_LENGTH]; this->go_deeper_fxn_ = &tesseract::Dict::go_deeper_dawg_fxn; int attempts_left = max_permuter_attempts; 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; 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 * preceding 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 tesseract-3.04.01/dict/stopper.cpp000066400000000000000000000500271266071204500170060ustar00rootroot00000000000000/****************************************************************************** ** Filename: stopper.c ** Purpose: Stopping criteria for word classifier. ** Author: Dan Johnson ** History: Mon Apr 29 14:56:49 1991, 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 #include #include #include #include "stopper.h" #include "ambigs.h" #include "ccutil.h" #include "const.h" #include "danerror.h" #include "dict.h" #include "efio.h" #include "helpers.h" #include "matchdefs.h" #include "pageres.h" #include "params.h" #include "ratngs.h" #include "scanutils.h" #include "unichar.h" #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #pragma warning(disable:4800) // int/bool warnings #endif using tesseract::ScriptPos; /*---------------------------------------------------------------------------- Private Code ----------------------------------------------------------------------------*/ namespace tesseract { bool Dict::AcceptableChoice(const WERD_CHOICE& best_choice, XHeightConsistencyEnum xheight_consistency) { float CertaintyThreshold = stopper_nondict_certainty_base; int WordSize; if (stopper_no_acceptable_choices) return false; if (best_choice.length() == 0) return false; 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) { 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'), 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(best_choice); WordSize -= stopper_smallword_size; if (WordSize < 0) WordSize = 0; CertaintyThreshold += WordSize * stopper_certainty_per_char; } if (stopper_debug_level >= 1) tprintf("Stopper: Rating = %4.1f, Certainty = %4.1f, Threshold = %4.1f\n", best_choice.rating(), best_choice.certainty(), CertaintyThreshold); if (no_dang_ambigs && best_choice.certainty() > CertaintyThreshold && xheight_consistency < XH_INCONSISTENT && UniformCertainties(best_choice)) { return true; } else { if (stopper_debug_level >= 1) { tprintf("AcceptableChoice() returned false" " (no_dang_ambig:%d cert:%.4g thresh:%g uniform:%d)\n", no_dang_ambigs, best_choice.certainty(), CertaintyThreshold, UniformCertainties(best_choice)); } return false; } } 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, 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 (word->best_choice->length() == 0 || !word->best_choices.singleton()) return false; 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; CertaintyThreshold += WordSize * stopper_certainty_per_char; } if (stopper_debug_level >= 1) tprintf("Rejecter: Certainty = %4.1f, Threshold = %4.1f ", word->best_choice->certainty(), CertaintyThreshold); if (word->best_choice->certainty() > CertaintyThreshold && !stopper_no_acceptable_choices) { if (stopper_debug_level >= 1) tprintf("ACCEPTED\n"); return true; } else { if (stopper_debug_level >= 1) tprintf("REJECTED\n"); return false; } } bool Dict::NoDangerousAmbig(WERD_CHOICE *best_choice, DANGERR *fixpt, bool fix_replaceable, MATRIX *ratings) { if (stopper_debug_level > 2) { tprintf("\nRunning NoDangerousAmbig() for %s\n", best_choice->debug_string().string()); } // Construct BLOB_CHOICE_LIST_VECTOR with ambiguities // for each unichar id in BestChoice. BLOB_CHOICE_LIST_VECTOR ambig_blob_choices; int i; bool ambigs_found = false; // For each position in best_choice: // -- choose AMBIG_SPEC_LIST that corresponds to unichar_id at best_choice[i] // -- initialize wrong_ngram with a single unichar_id at best_choice[i] // -- look for ambiguities corresponding to wrong_ngram in the list while // adding the following unichar_ids from best_choice to wrong_ngram // // Repeat the above procedure twice: first time look through // ambigs to be replaced and replace all the ambiguities found; // second time look through dangerous ambiguities and construct // ambig_blob_choices with fake a blob choice for each ambiguity // and pass them to dawg_permute_and_select() to search for // ambiguous words in the dictionaries. // // Note that during the execution of the for loop (on the first pass) // if replacements are made the length of best_choice might change. for (int pass = 0; pass < (fix_replaceable ? 2 : 1); ++pass) { bool replace = (fix_replaceable && pass == 0); const UnicharAmbigsVector &table = replace ? getUnicharAmbigs().replace_ambigs() : getUnicharAmbigs().dang_ambigs(); if (!replace) { // Initialize ambig_blob_choices with lists containing a single // unichar id for the correspoding position in best_choice. // best_choice consisting from only the original letters will // have a rating of 0.0. 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) 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, 0, 1, 0, BCC_AMBIG)); ambig_blob_choices.push_back(lst); } } UNICHAR_ID wrong_ngram[MAX_AMBIG_SIZE + 1]; int wrong_ngram_index; int next_index; int blob_index = 0; 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 || curr_unichar_id >= table.size() || table[curr_unichar_id] == NULL) { continue; // there is no ambig spec for this unichar id } AmbigSpec_IT spec_it(table[curr_unichar_id]); for (spec_it.mark_cycle_pt(); !spec_it.cycled_list();) { const AmbigSpec *ambig_spec = spec_it.data(); wrong_ngram[wrong_ngram_index+1] = INVALID_UNICHAR_ID; int compare = UnicharIdArrayUtils::compare(wrong_ngram, ambig_spec->wrong_ngram); if (stopper_debug_level > 2) { tprintf("candidate ngram: "); UnicharIdArrayUtils::print(wrong_ngram, getUnicharset()); tprintf("current ngram from spec: "); UnicharIdArrayUtils::print(ambig_spec->wrong_ngram, getUnicharset()); tprintf("comparison result: %d\n", compare); } 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 + 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 %s)\n", blob_index, blob_index + num_wrong_blobs, false, getUnicharset().get_isngram( ambig_spec->correct_ngram_id), getUnicharset().id_to_unichar(leftmost_id)); } } if (replace) { if (stopper_debug_level > 2) { 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, ratings); } else if (i > 0 || ambig_spec->type != CASE_AMBIG) { // We found dang ambig - update ambig_blob_choices. if (stopper_debug_level > 2) { tprintf("found ambiguity: "); UnicharIdArrayUtils::print( ambig_spec->correct_fragments, getUnicharset()); } ambigs_found = true; for (int tmp_index = 0; tmp_index <= wrong_ngram_index; ++tmp_index) { // Add a blob choice for the corresponding fragment of the // ambiguity. These fake blob choices are initialized with // negative ratings (which are not possible for real blob // choices), so that dawg_permute_and_select() considers any // word not consisting of only the original letters a better // choice and stops searching for alternatives once such a // choice is found. 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, 0, 1, 0, BCC_AMBIG)); } } spec_it.forward(); } else if (compare == -1) { if (wrong_ngram_index+1 < ambig_spec->wrong_ngram_size && ((next_index = wrong_ngram_index+1+i) < best_choice->length())) { // Add the next unichar id to wrong_ngram and keep looking for // 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 } } else { spec_it.forward(); } } // end searching AmbigSpec_LIST } // end searching best_choice } // end searching replace and dangerous ambigs // If any ambiguities were found permute the constructed ambig_blob_choices // to see if an alternative dictionary word can be found. if (ambigs_found) { if (stopper_debug_level > 2) { tprintf("\nResulting ambig_blob_choices:\n"); for (i = 0; i < ambig_blob_choices.length(); ++i) { print_ratings_list("", ambig_blob_choices.get(i), getUnicharset()); tprintf("\n"); } } WERD_CHOICE *alt_word = dawg_permute_and_select(ambig_blob_choices, 0.0); ambigs_found = (alt_word->rating() < 0.0); if (ambigs_found) { if (stopper_debug_level >= 1) { tprintf ("Stopper: Possible ambiguous word = %s\n", alt_word->debug_string().string()); } if (fixpt != NULL) { // Note: Currently character choices combined from fragments can only // be generated by NoDangrousAmbigs(). This code should be updated if // the capability to produce classifications combined from character // 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 = 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); } 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); } } } delete alt_word; } if (output_ambig_words_file_ != NULL) { fprintf(output_ambig_words_file_, "\n"); } ambig_blob_choices.delete_data_pointers(); return !ambigs_found; } void Dict::EndDangerousAmbigs() {} void Dict::SettupStopperPass1() { reject_offset_ = 0.0; } void Dict::SettupStopperPass2() { reject_offset_ = stopper_phase2_certainty_rejection_offset; } void Dict::ReplaceAmbig(int wrong_ngram_begin_index, int wrong_ngram_size, UNICHAR_ID correct_ngram_id, WERD_CHOICE *werd_choice, 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) { 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->state(i); } } 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); // DO NOT SORT!! It will mess up the iterator in LanguageModel::UpdateState. } 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); BLOB_CHOICE_IT it (new_choices); it.add_to_end(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 (replaced_count + 1 == wrong_ngram_size) { werd_choice->set_blob_choice(wrong_ngram_begin_index, num_blobs_to_replace, choice); } else { werd_choice->remove_unichar_id(wrong_ngram_begin_index + 1); } } if (stopper_debug_level >= 1) { werd_choice->print("ReplaceAmbig() "); tprintf("Modified blob_choices: "); print_ratings_list("\n", new_choices, getUnicharset()); } } int Dict::LengthOfShortestAlphaRun(const WERD_CHOICE &WordChoice) { int shortest = MAX_INT32; int curr_len = 0; for (int w = 0; w < WordChoice.length(); ++w) { if (getUnicharset().get_isalpha(WordChoice.unichar_id(w))) { curr_len++; } else if (curr_len > 0) { if (curr_len < shortest) shortest = curr_len; curr_len = 0; } } if (curr_len > 0 && curr_len < shortest) { shortest = curr_len; } else if (shortest == MAX_INT32) { shortest = 0; } return shortest; } int Dict::UniformCertainties(const WERD_CHOICE& word) { float Certainty; float WorstCertainty = MAX_FLOAT32; float CertaintyThreshold; FLOAT64 TotalCertainty; FLOAT64 TotalCertaintySquared; FLOAT64 Variance; FLOAT32 Mean, StdDev; int word_length = word.length(); if (word_length < 3) return true; TotalCertainty = TotalCertaintySquared = 0.0; for (int i = 0; i < word_length; ++i) { Certainty = word.certainty(i); TotalCertainty += Certainty; TotalCertaintySquared += Certainty * Certainty; if (Certainty < WorstCertainty) WorstCertainty = Certainty; } // Subtract off worst certainty from statistics. word_length--; TotalCertainty -= WorstCertainty; TotalCertaintySquared -= WorstCertainty * WorstCertainty; Mean = TotalCertainty / word_length; Variance = ((word_length * TotalCertaintySquared - TotalCertainty * TotalCertainty) / (word_length * (word_length - 1))); if (Variance < 0.0) Variance = 0.0; StdDev = sqrt(Variance); CertaintyThreshold = Mean - stopper_allowable_character_badness * StdDev; if (CertaintyThreshold > stopper_nondict_certainty_base) CertaintyThreshold = stopper_nondict_certainty_base; if (word.certainty() < CertaintyThreshold) { if (stopper_debug_level >= 1) tprintf("Stopper: Non-uniform certainty = %4.1f" " (m=%4.1f, s=%4.1f, t=%4.1f)\n", word.certainty(), Mean, StdDev, CertaintyThreshold); return false; } else { return true; } } } // namespace tesseract tesseract-3.04.01/dict/stopper.h000066400000000000000000000034031266071204500164470ustar00rootroot00000000000000/****************************************************************************** ** Filename: stopper.h ** Purpose: Stopping criteria for word classifier. ** Author: Dan Johnson ** History: Wed May 1 09:42:57 1991, 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 STOPPER_H #define STOPPER_H /*---------------------------------------------------------------------------- Include Files and Type Defines ----------------------------------------------------------------------------*/ #include "genericvector.h" #include "params.h" #include "ratngs.h" #include "unichar.h" class WERD_CHOICE; typedef uinT8 BLOB_WIDTH; struct DANGERR_INFO { DANGERR_INFO() : 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; #endif tesseract-3.04.01/dict/trie.cpp000066400000000000000000000664741266071204500162720ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: trie.c (Formerly trie.c) * Description: Functions to build a trie data structure. * Author: Mark Seaman, OCR Technology * Created: Fri Oct 16 14:37:00 1987 * Modified: Fri Jul 26 12:18:10 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 ----------------------------------------------------------------------*/ #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #pragma warning(disable:4800) // int/bool warnings #endif #include "trie.h" #include "callcpp.h" #include "dawg.h" #include "dict.h" #include "freelist.h" #include "genericvector.h" #include "helpers.h" #include "kdpair.h" namespace tesseract { const char kDoNotReverse[] = "RRP_DO_NO_REVERSE"; const char kReverseIfHasRTL[] = "RRP_REVERSE_IF_HAS_RTL"; const char kForceReverse[] = "RRP_FORCE_REVERSE"; const char * const RTLReversePolicyNames[] = { kDoNotReverse, kReverseIfHasRTL, kForceReverse }; const char Trie::kAlphaPatternUnicode[] = "\u2000"; const char Trie::kDigitPatternUnicode[] = "\u2001"; const char Trie::kAlphanumPatternUnicode[] = "\u2002"; const char Trie::kPuncPatternUnicode[] = "\u2003"; const char Trie::kLowerPatternUnicode[] = "\u2004"; const char Trie::kUpperPatternUnicode[] = "\u2005"; const char *Trie::get_reverse_policy_name(RTLReversePolicy reverse_policy) { return RTLReversePolicyNames[reverse_policy]; } // Reset the Trie to empty. void Trie::clear() { nodes_.delete_data_pointers(); nodes_.clear(); root_back_freelist_.clear(); num_edges_ = 0; new_dawg_node(); // Need to allocate node 0. } bool Trie::edge_char_of(NODE_REF node_ref, NODE_REF next_node, int direction, bool word_end, UNICHAR_ID unichar_id, EDGE_RECORD **edge_ptr, EDGE_INDEX *edge_index) const { if (debug_level_ == 3) { tprintf("edge_char_of() given node_ref " REFFORMAT " next_node " REFFORMAT " direction %d word_end %d unichar_id %d, exploring node:\n", node_ref, next_node, direction, word_end, unichar_id); if (node_ref != NO_EDGE) { print_node(node_ref, nodes_[node_ref]->forward_edges.size()); } } if (node_ref == NO_EDGE) return false; assert(node_ref < nodes_.size()); 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 && direction == FORWARD_EDGE) { // binary search EDGE_INDEX start = 0; EDGE_INDEX end = vec_size - 1; EDGE_INDEX k; int compare; while (start <= end) { k = (start + end) >> 1; // (start + end) / 2 compare = given_greater_than_edge_rec(next_node, word_end, unichar_id, vec[k]); if (compare == 0) { // given == vec[k] *edge_ptr = &(vec[k]); *edge_index = k; return true; } else if (compare == 1) { // given > vec[k] start = k + 1; } else { // given < vec[k] end = k - 1; } } } else { // linear search for (int i = 0; i < vec_size; ++i) { EDGE_RECORD &edge_rec = vec[i]; if (edge_rec_match(next_node, word_end, unichar_id, next_node_from_edge_rec(edge_rec), end_of_word_from_edge_rec(edge_rec), unichar_id_from_edge_rec(edge_rec))) { *edge_ptr = &(edge_rec); *edge_index = i; return true; } } } return false; // not found } bool Trie::add_edge_linkage(NODE_REF node1, NODE_REF node2, bool marker_flag, int direction, bool word_end, UNICHAR_ID unichar_id) { EDGE_VECTOR *vec = (direction == FORWARD_EDGE) ? &(nodes_[node1]->forward_edges) : &(nodes_[node1]->backward_edges); int search_index; 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, (*vec)[search_index]) == 1) { search_index++; } } else { search_index = vec->size(); // add is unsorted, so index does not matter } EDGE_RECORD edge_rec; link_edge(&edge_rec, node2, marker_flag, direction, word_end, unichar_id); 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); } if (debug_level_ > 1) { tprintf("new edge in nodes_[" REFFORMAT "]: ", node1); print_edge_rec(edge_rec); tprintf("\n"); } num_edges_++; return true; } void Trie::add_word_ending(EDGE_RECORD *edge_ptr, NODE_REF the_next_node, bool marker_flag, UNICHAR_ID unichar_id) { EDGE_RECORD *back_edge_ptr; EDGE_INDEX back_edge_index; ASSERT_HOST(edge_char_of(the_next_node, NO_EDGE, BACKWARD_EDGE, false, unichar_id, &back_edge_ptr, &back_edge_index)); if (marker_flag) { *back_edge_ptr |= (MARKER_FLAG << flag_start_bit_); *edge_ptr |= (MARKER_FLAG << flag_start_bit_); } // Mark both directions as end of word. *back_edge_ptr |= (WERD_END_FLAG << flag_start_bit_); *edge_ptr |= (WERD_END_FLAG << flag_start_bit_); } bool Trie::add_word_to_dawg(const WERD_CHOICE &word, const GenericVector *repetitions) { if (word.length() <= 0) return false; // can't add empty words if (repetitions != NULL) ASSERT_HOST(repetitions->size() == word.length()); // Make sure the word does not contain invalid unchar ids. for (int i = 0; i < word.length(); ++i) { if (word.unichar_id(i) < 0 || word.unichar_id(i) >= unicharset_size_) return false; } EDGE_RECORD *edge_ptr; NODE_REF last_node = 0; NODE_REF the_next_node; bool marker_flag = false; EDGE_INDEX edge_index; int i; inT32 still_finding_chars = true; inT32 word_end = false; bool add_failed = false; bool found; if (debug_level_ > 1) word.print("\nAdding word: "); UNICHAR_ID unichar_id; for (i = 0; i < word.length() - 1; ++i) { unichar_id = word.unichar_id(i); marker_flag = (repetitions != NULL) ? (*repetitions)[i] : false; if (debug_level_ > 1) tprintf("Adding letter %d\n", unichar_id); if (still_finding_chars) { found = edge_char_of(last_node, NO_EDGE, FORWARD_EDGE, word_end, unichar_id, &edge_ptr, &edge_index); if (found && debug_level_ > 1) { tprintf("exploring edge " REFFORMAT " in node " REFFORMAT "\n", edge_index, last_node); } 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); } } if (!still_finding_chars) { the_next_node = new_dawg_node(); if (debug_level_ > 1) tprintf("adding node " REFFORMAT "\n", the_next_node); if (the_next_node == 0) { add_failed = true; break; } if (!add_new_edge(last_node, the_next_node, marker_flag, word_end, unichar_id)) { add_failed = true; break; } word_end = false; last_node = the_next_node; } } the_next_node = 0; unichar_id = word.unichar_id(i); marker_flag = (repetitions != NULL) ? (*repetitions)[i] : false; if (debug_level_ > 1) tprintf("Adding letter %d\n", unichar_id); if (still_finding_chars && edge_char_of(last_node, NO_EDGE, FORWARD_EDGE, false, unichar_id, &edge_ptr, &edge_index)) { // An extension of this word already exists in the trie, so we // only have to add the ending flags in both directions. 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; } if (add_failed) { tprintf("Re-initializing document dictionary...\n"); clear(); return false; } else { return true; } } NODE_REF Trie::new_dawg_node() { TRIE_NODE_RECORD *node = new TRIE_NODE_RECORD(); if (node == NULL) return 0; // failed to create new node nodes_.push_back(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, GenericVector* words) { FILE *word_file; char string[CHARS_PER_LINE]; int word_count = 0; 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 WERD_CHOICE word(string, unicharset); if ((reverse_policy == RRP_REVERSE_IF_HAS_RTL && word.has_rtl_unichar_id()) || reverse_policy == RRP_FORCE_REVERSE) { word.reverse_and_mirror_unichar_ids(); } ++word_count; 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)) { words->push_back(word.unichar_string()); } else if (debug_level_) { tprintf("Skipping invalid word %s\n", string); if (debug_level_ >= 3) word.print(); } } if (debug_level_) tprintf("Read %d words total.\n", word_count); fclose(word_file); 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); unicharset->unichar_insert(kDigitPatternUnicode); digit_pattern_ = unicharset->unichar_to_id(kDigitPatternUnicode); unicharset->unichar_insert(kAlphanumPatternUnicode); alphanum_pattern_ = unicharset->unichar_to_id(kAlphanumPatternUnicode); unicharset->unichar_insert(kPuncPatternUnicode); punc_pattern_ = unicharset->unichar_to_id(kPuncPatternUnicode); unicharset->unichar_insert(kLowerPatternUnicode); lower_pattern_ = unicharset->unichar_to_id(kLowerPatternUnicode); unicharset->unichar_insert(kUpperPatternUnicode); upper_pattern_ = unicharset->unichar_to_id(kUpperPatternUnicode); initialized_patterns_ = true; unicharset_size_ = unicharset->size(); } void Trie::unichar_id_to_patterns(UNICHAR_ID unichar_id, const UNICHARSET &unicharset, GenericVector *vec) const { bool is_alpha = unicharset.get_isalpha(unichar_id); if (is_alpha) { vec->push_back(alpha_pattern_); vec->push_back(alphanum_pattern_); if (unicharset.get_islower(unichar_id)) { vec->push_back(lower_pattern_); } else if (unicharset.get_isupper(unichar_id)) { vec->push_back(upper_pattern_); } } if (unicharset.get_isdigit(unichar_id)) { vec->push_back(digit_pattern_); if (!is_alpha) vec->push_back(alphanum_pattern_); } if (unicharset.get_ispunctuation(unichar_id)) { vec->push_back(punc_pattern_); } } UNICHAR_ID Trie::character_class_to_pattern(char ch) { if (ch == 'c') { return alpha_pattern_; } else if (ch == 'd') { return digit_pattern_; } else if (ch == 'n') { return alphanum_pattern_; } else if (ch == 'p') { return punc_pattern_; } else if (ch == 'a') { return lower_pattern_; } else if (ch == 'A') { return upper_pattern_; } else { return INVALID_UNICHAR_ID; } } bool Trie::read_pattern_list(const char *filename, const UNICHARSET &unicharset) { if (!initialized_patterns_) { tprintf("please call initialize_patterns() before read_pattern_list()\n"); return false; } FILE *pattern_file = fopen(filename, "rb"); if (pattern_file == NULL) { tprintf("Error opening pattern file %s\n", filename); return false; } int pattern_count = 0; char string[CHARS_PER_LINE]; while (fgets(string, CHARS_PER_LINE, pattern_file) != NULL) { chomp_string(string); // remove newline // Parse the pattern and construct a unichar id vector. // Record the number of repetitions of each unichar in the parallel vector. WERD_CHOICE word(&unicharset); GenericVector repetitions_vec; const char *str_ptr = string; int step = unicharset.step(str_ptr); bool failed = false; while (step > 0) { UNICHAR_ID curr_unichar_id = INVALID_UNICHAR_ID; if (step == 1 && *str_ptr == '\\') { ++str_ptr; if (*str_ptr == '\\') { // regular '\' unichar that was escaped curr_unichar_id = unicharset.unichar_to_id(str_ptr, step); } else { if (word.length() < kSaneNumConcreteChars) { tprintf("Please provide at least %d concrete characters at the" " beginning of the pattern\n", kSaneNumConcreteChars); failed = true; break; } // Parse character class from expression. curr_unichar_id = character_class_to_pattern(*str_ptr); } } else { curr_unichar_id = unicharset.unichar_to_id(str_ptr, step); } if (curr_unichar_id == INVALID_UNICHAR_ID) { failed = true; break; // failed to parse this pattern } word.append_unichar_id(curr_unichar_id, 1, 0.0, 0.0); repetitions_vec.push_back(false); str_ptr += step; step = unicharset.step(str_ptr); // Check if there is a repetition pattern specified after this unichar. if (step == 1 && *str_ptr == '\\' && *(str_ptr+1) == '*') { repetitions_vec[repetitions_vec.size()-1] = true; str_ptr += 2; step = unicharset.step(str_ptr); } } if (failed) { tprintf("Invalid user pattern %s\n", string); continue; } // Insert the pattern into the trie. if (debug_level_ > 2) { tprintf("Inserting expanded user pattern %s\n", word.debug_string().string()); } if (!this->word_in_dawg(word)) { this->add_word_to_dawg(word, &repetitions_vec); if (!this->word_in_dawg(word)) { tprintf("Error: failed to insert pattern '%s'\n", string); } } ++pattern_count; } if (debug_level_) { tprintf("Read %d valid patterns from %s\n", pattern_count, filename); } fclose(pattern_file); return true; } void Trie::remove_edge_linkage(NODE_REF node1, NODE_REF node2, int direction, bool word_end, UNICHAR_ID unichar_id) { EDGE_RECORD *edge_ptr = NULL; EDGE_INDEX edge_index = 0; ASSERT_HOST(edge_char_of(node1, node2, direction, word_end, unichar_id, &edge_ptr, &edge_index)); if (debug_level_ > 1) { tprintf("removed edge in nodes_[" REFFORMAT "]: ", node1); print_edge_rec(*edge_ptr); tprintf("\n"); } 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); } NODE_MARKER reduced_nodes = new bool[nodes_.size()]; for (int i = 0; i < nodes_.size(); i++) reduced_nodes[i] = 0; this->reduce_node_input(0, reduced_nodes); delete[] reduced_nodes; if (debug_level_ > 2) { print_all("After reduction:", MAX_NODE_EDGES_DISPLAY); } // Build a translation map from node indices in nodes_ vector to // their target indices in EDGE_ARRAY. NODE_REF *node_ref_map = new NODE_REF[nodes_.size() + 1]; int i, j; node_ref_map[0] = 0; for (i = 0; i < nodes_.size(); ++i) { node_ref_map[i+1] = node_ref_map[i] + nodes_[i]->forward_edges.size(); } int num_forward_edges = node_ref_map[i]; // Convert nodes_ vector into EDGE_ARRAY translating the next node references // in edges using node_ref_map. Empty nodes and backward edges are dropped. EDGE_ARRAY edge_array = (EDGE_ARRAY)memalloc(num_forward_edges * sizeof(EDGE_RECORD)); EDGE_ARRAY edge_array_ptr = edge_array; for (i = 0; i < nodes_.size(); ++i) { TRIE_NODE_RECORD *node_ptr = nodes_[i]; int end = node_ptr->forward_edges.size(); for (j = 0; j < end; ++j) { EDGE_RECORD &edge_rec = node_ptr->forward_edges[j]; NODE_REF node_ref = next_node_from_edge_rec(edge_rec); ASSERT_HOST(node_ref < nodes_.size()); UNICHAR_ID unichar_id = unichar_id_from_edge_rec(edge_rec); link_edge(edge_array_ptr, node_ref_map[node_ref], false, FORWARD_EDGE, end_of_word_from_edge_rec(edge_rec), unichar_id); if (j == end - 1) set_marker_flag_in_edge_rec(edge_array_ptr); ++edge_array_ptr; } } delete[] node_ref_map; return new SquishedDawg(edge_array, num_forward_edges, type_, lang_, perm_, unicharset_size_, debug_level_); } bool Trie::eliminate_redundant_edges(NODE_REF node, const EDGE_RECORD &edge1, const EDGE_RECORD &edge2) { if (debug_level_ > 1) { tprintf("\nCollapsing node %d:\n", node); print_node(node, MAX_NODE_EDGES_DISPLAY); tprintf("Candidate edges: "); print_edge_rec(edge1); tprintf(", "); print_edge_rec(edge2); tprintf("\n\n"); } NODE_REF next_node1 = next_node_from_edge_rec(edge1); NODE_REF next_node2 = next_node_from_edge_rec(edge2); TRIE_NODE_RECORD *next_node2_ptr = nodes_[next_node2]; // Translate all edges going to/from next_node2 to go to/from next_node1. EDGE_RECORD *edge_ptr = NULL; EDGE_INDEX edge_index; int i; // 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]; NODE_REF curr_next_node = next_node_from_edge_rec(bkw_edge); UNICHAR_ID curr_unichar_id = unichar_id_from_edge_rec(bkw_edge); int curr_word_end = end_of_word_from_edge_rec(bkw_edge); bool marker_flag = marker_flag_from_edge_rec(bkw_edge); add_edge_linkage(next_node1, curr_next_node, marker_flag, BACKWARD_EDGE, curr_word_end, curr_unichar_id); // Relocate the corresponding forward edge in curr_next_node ASSERT_HOST(edge_char_of(curr_next_node, next_node2, FORWARD_EDGE, curr_word_end, curr_unichar_id, &edge_ptr, &edge_index)); set_next_node_in_edge_rec(edge_ptr, next_node1); } int next_node2_num_edges = (next_node2_ptr->forward_edges.size() + next_node2_ptr->backward_edges.size()); if (debug_level_ > 1) { tprintf("removed %d edges from node " REFFORMAT "\n", next_node2_num_edges, next_node2); } next_node2_ptr->forward_edges.clear(); next_node2_ptr->backward_edges.clear(); num_edges_ -= next_node2_num_edges; return true; } bool Trie::reduce_lettered_edges(EDGE_INDEX edge_index, UNICHAR_ID unichar_id, NODE_REF node, 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) { // Find the first edge that can be eliminated. UNICHAR_ID curr_unichar_id = INVALID_UNICHAR_ID; while (i < backward_edges->size()) { if (!DeadEdge((*backward_edges)[i])) { curr_unichar_id = unichar_id_from_edge_rec((*backward_edges)[i]); 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 (DeadEdge(next_edge_rec)) continue; UNICHAR_ID next_id = unichar_id_from_edge_rec(next_edge_rec); 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; KillEdge(&(*backward_edges)[j]); } } } return did_something; } void Trie::sort_edges(EDGE_VECTOR *edges) { int num_edges = edges->size(); if (num_edges <= 1) return; 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, NODE_MARKER reduced_nodes) { EDGE_VECTOR &backward_edges = nodes_[node]->backward_edges; sort_edges(&backward_edges); if (debug_level_ > 1) { tprintf("reduce_node_input(node=" REFFORMAT ")\n", node); print_node(node, MAX_NODE_EDGES_DISPLAY); } EDGE_INDEX edge_index = 0; while (edge_index < backward_edges.size()) { if (DeadEdge(backward_edges[edge_index])) continue; 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 id = unichar_id_from_edge_rec(backward_edges[edge_index]); if (!DeadEdge(backward_edges[edge_index]) && id != unichar_id) break; } } reduced_nodes[node] = true; // mark as reduced if (debug_level_ > 1) { tprintf("Node " REFFORMAT " after reduction:\n", node); print_node(node, MAX_NODE_EDGES_DISPLAY); } for (int i = 0; i < backward_edges.size(); ++i) { if (DeadEdge(backward_edges[i])) continue; NODE_REF next_node = next_node_from_edge_rec(backward_edges[i]); if (next_node != 0 && !reduced_nodes[next_node]) { reduce_node_input(next_node, reduced_nodes); } } } void Trie::print_node(NODE_REF node, int max_num_edges) const { if (node == NO_EDGE) return; // nothing to print TRIE_NODE_RECORD *node_ptr = nodes_[node]; int num_fwd = node_ptr->forward_edges.size(); int num_bkw = node_ptr->backward_edges.size(); EDGE_VECTOR *vec; for (int dir = 0; dir < 2; ++dir) { if (dir == 0) { vec = &(node_ptr->forward_edges); tprintf(REFFORMAT " (%d %d): ", node, num_fwd, num_bkw); } else { vec = &(node_ptr->backward_edges); tprintf("\t"); } int i; for (i = 0; (dir == 0 ? i < num_fwd : i < num_bkw) && i < max_num_edges; ++i) { if (DeadEdge((*vec)[i])) continue; print_edge_rec((*vec)[i]); tprintf(" "); } if (dir == 0 ? i < num_fwd : i < num_bkw) tprintf("..."); tprintf("\n"); } } } // namespace tesseract tesseract-3.04.01/dict/trie.h000066400000000000000000000467201266071204500157270ustar00rootroot00000000000000/* -*-C-*- ******************************************************************************** * * File: trie.h (Formerly trie.h) * Description: Functions to build a trie data structure. * Author: Mark Seaman, SW Productivity * Created: Fri Oct 16 14:37:00 1987 * Modified: Fri Jul 26 11:26:34 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 TRIE_H #define TRIE_H #include "dawg.h" #include "cutil.h" #include "genericvector.h" class UNICHARSET; // Note: if we consider either NODE_REF or EDGE_INDEX to ever exceed // max int32, we will need to change GenericVector to use int64 for size // and address indices. This does not seem to be needed immediately, // since currently the largest number of edges limit used by tesseract // (kMaxNumEdges in wordlist2dawg.cpp) is far less than max int32. // There are also int casts below to satisfy the WIN32 compiler that would // need to be changed. // It might be cleanest to change the types of most of the Trie/Dawg related // typedefs to int and restrict the casts to extracting these values from // the 64 bit EDGE_RECORD. typedef inT64 EDGE_INDEX; // index of an edge in a given node typedef bool *NODE_MARKER; typedef GenericVector EDGE_VECTOR; struct TRIE_NODE_RECORD { EDGE_VECTOR forward_edges; EDGE_VECTOR backward_edges; }; typedef GenericVector TRIE_NODES; namespace tesseract { /** * Concrete class for Trie data structure that allows to store a list of * words (extends Dawg base class) as well as dynamically add new words. * This class stores a vector of pointers to TRIE_NODE_RECORDs, each of * which has a vector of forward and backward edges. */ class Trie : public Dawg { public: enum RTLReversePolicy { RRP_DO_NO_REVERSE, RRP_REVERSE_IF_HAS_RTL, RRP_FORCE_REVERSE, }; // Minimum number of concrete characters at the beginning of user patterns. static const int kSaneNumConcreteChars = 0; // Various unicode whitespace characters are used to denote unichar patterns, // (character classifier would never produce these whitespace characters as a // valid classification). static const char kAlphaPatternUnicode[]; static const char kDigitPatternUnicode[]; static const char kAlphanumPatternUnicode[]; static const char kPuncPatternUnicode[]; static const char kLowerPatternUnicode[]; static const char kUpperPatternUnicode[]; static const char *get_reverse_policy_name( RTLReversePolicy reverse_policy); // max_num_edges argument allows limiting the amount of memory this // Trie can consume (if a new word insert would cause the Trie to // contain more edges than max_num_edges, all the edges are cleared // so that new inserts can proceed). Trie(DawgType type, const STRING &lang, PermuterType perm, int unicharset_size, int debug_level) { init(type, lang, perm, unicharset_size, debug_level); num_edges_ = 0; deref_node_index_mask_ = ~letter_mask_; new_dawg_node(); // need to allocate node 0 initialized_patterns_ = false; } virtual ~Trie() { nodes_.delete_data_pointers(); } // Reset the Trie to empty. void clear(); /** Returns the edge that corresponds to the letter out of this node. */ EDGE_REF edge_char_of(NODE_REF node_ref, UNICHAR_ID unichar_id, bool word_end) const { EDGE_RECORD *edge_ptr; EDGE_INDEX edge_index; if (!edge_char_of(node_ref, NO_EDGE, FORWARD_EDGE, word_end, unichar_id, &edge_ptr, &edge_index)) return NO_EDGE; return make_edge_ref(node_ref, edge_index); } /** * 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, bool word_end) const { const EDGE_VECTOR &forward_edges = nodes_[static_cast(node)]->forward_edges; for (int i = 0; i < forward_edges.size(); ++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))); } } } /** * Returns the next node visited by following the edge * indicated by the given EDGE_REF. */ NODE_REF next_node(EDGE_REF edge_ref) const { if (edge_ref == NO_EDGE || num_edges_ == 0) return NO_EDGE; return next_node_from_edge_rec(*deref_edge_ref(edge_ref)); } /** * Returns true if the edge indicated by the given EDGE_REF * marks the end of a word. */ bool end_of_word(EDGE_REF edge_ref) const { if (edge_ref == NO_EDGE || num_edges_ == 0) return false; return end_of_word_from_edge_rec(*deref_edge_ref(edge_ref)); } /** Returns UNICHAR_ID stored in the edge indicated by the given EDGE_REF. */ UNICHAR_ID edge_letter(EDGE_REF edge_ref) const { 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 unicharset_size_, marking // the edge dead. void KillEdge(EDGE_RECORD* edge_rec) const { *edge_rec &= ~letter_mask_; *edge_rec |= (unicharset_size_ << LETTER_START_BIT); } bool DeadEdge(const EDGE_RECORD& edge_rec) const { return unichar_id_from_edge_rec(edge_rec) == unicharset_size_; } // Prints the contents of the node indicated by the given NODE_REF. // At most max_num_edges will be printed. void print_node(NODE_REF node, int max_num_edges) const; // Writes edges from nodes_ to an EDGE_ARRAY and creates a SquishedDawg. // Eliminates redundant edges and returns the pointer to the SquishedDawg. // Note: the caller is responsible for deallocating memory associated // with the returned SquishedDawg pointer. SquishedDawg *trie_to_dawg(); // 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_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. // // Each pattern can contain any non-whitespace characters, however only the // patterns that contain characters from the unicharset of the corresponding // language will be useful. // The only meta character is '\'. To be used in a pattern as an ordinary // string it should be escaped with '\' (e.g. string "C:\Documents" should // be written in the patterns file as "C:\\Documents"). // This function supports a very limited regular expression syntax. One can // express a character, a certain character class and a number of times the // entity should be repeated in the pattern. // // To denote a character class use one of: // \c - unichar for which UNICHARSET::get_isalpha() is true (character) // \d - unichar for which UNICHARSET::get_isdigit() is true // \n - unichar for which UNICHARSET::get_isdigit() and // UNICHARSET::isalpha() are true // \p - unichar for which UNICHARSET::get_ispunct() is true // \a - unichar for which UNICHARSET::get_islower() is true // \A - unichar for which UNICHARSET::get_isupper() is true // // \* could be specified after each character or pattern to indicate that // the character/pattern can be repeated any number of times before the next // character/pattern occurs. // // Examples: // 1-8\d\d-GOOG-411 will be expanded to strings: // 1-800-GOOG-411, 1-801-GOOG-411, ... 1-899-GOOG-411. // // http://www.\n\*.com will be expanded to strings like: // http://www.a.com http://www.a123.com ... http://www.ABCDefgHIJKLMNop.com // // Note: In choosing which patterns to include please be aware of the fact // providing very generic patterns will make tesseract run slower. // For example \n\* at the beginning of the pattern will make Tesseract // consider all the combinations of proposed character choices for each // of the segmentations, which will be unacceptably slow. // Because of potential problems with speed that could be difficult to // identify, each user pattern has to have at least kSaneNumConcreteChars // concrete characters from the unicharset at the beginning. bool read_pattern_list(const char *filename, const UNICHARSET &unicharset); // Initializes the values of *_pattern_ unichar ids. // This function should be called before calling read_pattern_list(). void initialize_patterns(UNICHARSET *unicharset); // Fills in the given unichar id vector with the unichar ids that represent // the patterns of the character classes of the given unichar_id. void unichar_id_to_patterns(UNICHAR_ID unichar_id, const UNICHARSET &unicharset, GenericVector *vec) const; // Returns the given EDGE_REF if the EDGE_RECORD that it points to has // a self loop and the given unichar_id matches the unichar_id stored in the // EDGE_RECORD, returns NO_EDGE otherwise. virtual EDGE_REF pattern_loop_edge(EDGE_REF edge_ref, UNICHAR_ID unichar_id, bool word_end) const { if (edge_ref == NO_EDGE) return NO_EDGE; EDGE_RECORD *edge_rec = deref_edge_ref(edge_ref); return (marker_flag_from_edge_rec(*edge_rec) && unichar_id == unichar_id_from_edge_rec(*edge_rec) && word_end == end_of_word_from_edge_rec(*edge_rec)) ? edge_ref : NO_EDGE; } // Adds a word to the Trie (creates the necessary nodes and edges). // // If repetitions vector is not NULL, each entry in the vector indicates // whether the unichar id with the corresponding index in the word is allowed // to repeat an unlimited number of times. For each entry that is true, MARKER // flag of the corresponding edge created for this unichar id is set to true). // // Return true if add succeeded, false otherwise (e.g. when a word contained // an invalid unichar id or the trie was getting too large and was cleared). bool add_word_to_dawg(const WERD_CHOICE &word, const GenericVector *repetitions); bool add_word_to_dawg(const WERD_CHOICE &word) { return add_word_to_dawg(word, NULL); } protected: // The structure of an EDGE_REF for Trie edges is as follows: // [LETTER_START_BIT, flag_start_bit_): // edge index in *_edges in a TRIE_NODE_RECORD // [flag_start_bit, 30th bit]: node index in nodes (TRIE_NODES vector) // // With this arrangement there are enough bits to represent edge indices // (each node can have at most unicharset_size_ forward edges and // the position of flag_start_bit is set to be log2(unicharset_size_)). // It is also possible to accommodate a maximum number of nodes that is at // least as large as that of the SquishedDawg representation (in SquishedDawg // each EDGE_RECORD has 32-(flag_start_bit+NUM_FLAG_BITS) bits to represent // the next node index). // // Returns the pointer to EDGE_RECORD after decoding the location // of the edge from the information in the given EDGE_REF. // This function assumes that EDGE_REF holds valid node/edge indices. inline EDGE_RECORD *deref_edge_ref(EDGE_REF edge_ref) const { int edge_index = static_cast( (edge_ref & letter_mask_) >> LETTER_START_BIT); int node_index = static_cast( (edge_ref & deref_node_index_mask_) >> flag_start_bit_); TRIE_NODE_RECORD *node_rec = nodes_[node_index]; return &(node_rec->forward_edges[edge_index]); } /** Constructs EDGE_REF from the given node_index and edge_index. */ inline EDGE_REF make_edge_ref(NODE_REF node_index, EDGE_INDEX edge_index) const { return ((node_index << flag_start_bit_) | (edge_index << LETTER_START_BIT)); } /** Sets up this edge record to the requested values. */ inline void link_edge(EDGE_RECORD *edge, NODE_REF nxt, bool repeats, int direction, bool word_end, UNICHAR_ID unichar_id) { EDGE_RECORD flags = 0; if (repeats) flags |= MARKER_FLAG; if (word_end) flags |= WERD_END_FLAG; if (direction == BACKWARD_EDGE) flags |= DIRECTION_FLAG; *edge = ((nxt << next_node_start_bit_) | (static_cast(flags) << flag_start_bit_) | (static_cast(unichar_id) << LETTER_START_BIT)); } /** Prints the given EDGE_RECORD. */ inline void print_edge_rec(const EDGE_RECORD &edge_rec) const { tprintf("|" REFFORMAT "|%s%s%s|%d|", next_node_from_edge_rec(edge_rec), marker_flag_from_edge_rec(edge_rec) ? "R," : "", (direction_from_edge_rec(edge_rec) == FORWARD_EDGE) ? "F" : "B", end_of_word_from_edge_rec(edge_rec) ? ",E" : "", unichar_id_from_edge_rec(edge_rec)); } // Returns true if the next node in recorded the given EDGE_RECORD // has exactly one forward edge. inline bool can_be_eliminated(const EDGE_RECORD &edge_rec) { NODE_REF node_ref = next_node_from_edge_rec(edge_rec); return (node_ref != NO_EDGE && nodes_[static_cast(node_ref)]->forward_edges.size() == 1); } // Prints the contents of the Trie. // At most max_num_edges will be printed for each node. void print_all(const char* msg, int max_num_edges) { tprintf("\n__________________________\n%s\n", msg); for (int i = 0; i < nodes_.size(); ++i) print_node(i, max_num_edges); tprintf("__________________________\n"); } // Finds the edge with the given direction, word_end and unichar_id // in the node indicated by node_ref. Fills in the pointer to the // EDGE_RECORD and the index of the edge with the the values // corresponding to the edge found. Returns true if an edge was found. bool edge_char_of(NODE_REF node_ref, NODE_REF next_node, int direction, bool word_end, UNICHAR_ID unichar_id, EDGE_RECORD **edge_ptr, EDGE_INDEX *edge_index) const; // Adds an single edge linkage between node1 and node2 in the direction // indicated by direction argument. bool add_edge_linkage(NODE_REF node1, NODE_REF node2, bool repeats, int direction, bool word_end, UNICHAR_ID unichar_id); // Adds forward edge linkage from node1 to node2 and the corresponding // backward edge linkage in the other direction. bool add_new_edge(NODE_REF node1, NODE_REF node2, bool repeats, bool word_end, UNICHAR_ID unichar_id) { return (add_edge_linkage(node1, node2, repeats, FORWARD_EDGE, word_end, unichar_id) && add_edge_linkage(node2, node1, repeats, BACKWARD_EDGE, word_end, unichar_id)); } // Sets the word ending flags in an already existing edge pair. // Returns true on success. void add_word_ending(EDGE_RECORD *edge, NODE_REF the_next_node, bool repeats, UNICHAR_ID unichar_id); // Allocates space for a new node in the Trie. NODE_REF new_dawg_node(); // Removes a single edge linkage to between node1 and node2 in the // direction indicated by direction argument. void remove_edge_linkage(NODE_REF node1, NODE_REF node2, int direction, bool word_end, UNICHAR_ID unichar_id); // Removes forward edge linkage from node1 to node2 and the corresponding // backward edge linkage in the other direction. void remove_edge(NODE_REF node1, NODE_REF node2, bool word_end, UNICHAR_ID unichar_id) { remove_edge_linkage(node1, node2, FORWARD_EDGE, word_end, unichar_id); remove_edge_linkage(node2, node1, BACKWARD_EDGE, word_end, unichar_id); } // Compares edge1 and edge2 in the given node to see if they point to two // next nodes that could be collapsed. If they do, performs the reduction // and returns true. bool eliminate_redundant_edges(NODE_REF node, const EDGE_RECORD &edge1, const EDGE_RECORD &edge2); // Assuming that edge_index indicates the first edge in a group of edges // in this node with a particular letter value, looks through these edges // to see if any of them can be collapsed. If so does it. Returns to the // caller when all edges with this letter have been reduced. // Returns true if further reduction is possible with this same letter. bool reduce_lettered_edges(EDGE_INDEX edge_index, UNICHAR_ID unichar_id, NODE_REF node, EDGE_VECTOR* backward_edges, NODE_MARKER reduced_nodes); /** * Order num_edges of consequtive EDGE_RECORDS in the given EDGE_VECTOR in * increasing order of unichar ids. This function is normally called * for all edges in a single node, and since number of edges in each node * is usually quite small, selection sort is used. */ void sort_edges(EDGE_VECTOR *edges); /** Eliminates any redundant edges from this node in the Trie. */ void reduce_node_input(NODE_REF node, NODE_MARKER reduced_nodes); // Returns the pattern unichar id for the given character class code. UNICHAR_ID character_class_to_pattern(char ch); // Member variables TRIE_NODES nodes_; // vector of nodes in the Trie uinT64 num_edges_; // sum of all edges (forward and backward) 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_; UNICHAR_ID alpha_pattern_; UNICHAR_ID digit_pattern_; UNICHAR_ID alphanum_pattern_; UNICHAR_ID punc_pattern_; UNICHAR_ID lower_pattern_; UNICHAR_ID upper_pattern_; }; } // namespace tesseract #endif tesseract-3.04.01/doc/000077500000000000000000000000001266071204500144245ustar00rootroot00000000000000tesseract-3.04.01/doc/Doxyfile000066400000000000000000003124631266071204500161430ustar00rootroot00000000000000# Doxyfile 1.8.8 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = $(name) # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = $(version) # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo # to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = doc/ # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = $(builddir) # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = YES # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a # new page for each member. If set to NO, the documentation of a member will be # part of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: # Fortran. In the later case the parser tries to guess whether the code is fixed # or free formatted code, this is the default for Fortran type files), VHDL. For # instance to make doxygen treat .inc files as Fortran files (default is PHP), # and .f files as C (default is Fortran), use: inc=Fortran f=C. # # Note For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by by putting a % sign in front of the word # or globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined # locally in source files will be included in the documentation. If set to NO # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO these classes will be included in the various overviews. This option has # no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the # todo list. This list is created by putting \todo commands in the # documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the # test list. This list is created by putting \test commands in the # documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES the list # will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO doxygen will only warn about wrong or incomplete parameter # documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = YES # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = $(builddir)/doc/DoxyWarn.log #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. # Note: If this tag is empty the current directory is searched. INPUT = $(srcdir) # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank the # following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, # *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, # *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.d \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.idl \ *.odl \ *.cs \ *.php \ *.php3 \ *.inc \ *.m \ *.mm \ *.dox \ *.py \ *.f90 \ *.f \ *.vhd \ *.vhdl # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = vs2010 # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = */.svn/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER ) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = YES # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES, then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = NO # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefor more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra stylesheet files is of importance (e.g. the last # stylesheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the stylesheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to NO can help when comparing the output of multiple runs. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler ( hhc.exe). If non-empty # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated ( # YES) or that it should be included in the master .chm file ( NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated ( # YES) or a normal table of contents ( NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using prerendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /